• Socket与系统调用深度分析


    一、系统调用与socket编程

    系统调用是操作系统为用户态进程与硬件设备进行交互提供了一组接口。系统调用通过软中断向内核发出中断请求,int指令(interrupt)+具体的系统调用号触发中断请求。Socket的功能通过调用Socket API来实现。API(application program interface)可能直接提供用户态的的服务,也可能通过系统调用对应的封装例程(wrapper routine)调用一个或多个系统调用。

    在linux系统中,socket系统调用的API在/net/socket.c中定义。

    查看socket_call函数

    SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)
    {
        unsigned long a[AUDITSC_ARGS];
        unsigned long a0, a1;
        int err;
        unsigned int len;
    
        if (call < 1 || call > SYS_SENDMMSG)
            return -EINVAL;
        call = array_index_nospec(call, SYS_SENDMMSG + 1);
    
        len = nargs[call];
        if (len > sizeof(a))
            return -EINVAL;
    
        /* copy_from_user should be SMP safe. */
        if (copy_from_user(a, args, len))
            return -EFAULT;
    
        err = audit_socketcall(nargs[call] / sizeof(unsigned long), a);
        if (err)
            return err;
    
        a0 = a[0];
        a1 = a[1];
    
        switch (call) {
        case SYS_SOCKET:
            err = __sys_socket(a0, a1, a[2]);
            break;
        case SYS_BIND:
            err = __sys_bind(a0, (struct sockaddr __user *)a1, a[2]);
            break;
        case SYS_CONNECT:
            err = __sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
            break;
        case SYS_LISTEN:
            err = __sys_listen(a0, a1);
            break;
        case SYS_ACCEPT:
            err = __sys_accept4(a0, (struct sockaddr __user *)a1,
                        (int __user *)a[2], 0);
            break;
        case SYS_GETSOCKNAME:
            err =
                __sys_getsockname(a0, (struct sockaddr __user *)a1,
                          (int __user *)a[2]);
            break;
        case SYS_GETPEERNAME:
            err =
                __sys_getpeername(a0, (struct sockaddr __user *)a1,
                          (int __user *)a[2]);
            break;
        case SYS_SOCKETPAIR:
            err = __sys_socketpair(a0, a1, a[2], (int __user *)a[3]);
            break;
        case SYS_SEND:
            err = __sys_sendto(a0, (void __user *)a1, a[2], a[3],
                       NULL, 0);
            break;
        case SYS_SENDTO:
            err = __sys_sendto(a0, (void __user *)a1, a[2], a[3],
                       (struct sockaddr __user *)a[4], a[5]);
            break;
        case SYS_RECV:
            err = __sys_recvfrom(a0, (void __user *)a1, a[2], a[3],
                         NULL, NULL);
            break;
        case SYS_RECVFROM:
            err = __sys_recvfrom(a0, (void __user *)a1, a[2], a[3],
                         (struct sockaddr __user *)a[4],
                         (int __user *)a[5]);
            break;
        case SYS_SHUTDOWN:
            err = __sys_shutdown(a0, a1);
            break;
        case SYS_SETSOCKOPT:
            err = __sys_setsockopt(a0, a1, a[2], (char __user *)a[3],
                           a[4]);
            break;
        case SYS_GETSOCKOPT:
            err =
                __sys_getsockopt(a0, a1, a[2], (char __user *)a[3],
                         (int __user *)a[4]);
            break;
        case SYS_SENDMSG:
            err = __sys_sendmsg(a0, (struct user_msghdr __user *)a1,
                        a[2], true);
            break;
        case SYS_SENDMMSG:
            err = __sys_sendmmsg(a0, (struct mmsghdr __user *)a1, a[2],
                         a[3], true);
            break;
        case SYS_RECVMSG:
            err = __sys_recvmsg(a0, (struct user_msghdr __user *)a1,
                        a[2], true);
            break;
        case SYS_RECVMMSG:
            if (IS_ENABLED(CONFIG_64BIT) || !IS_ENABLED(CONFIG_64BIT_TIME))
                err = __sys_recvmmsg(a0, (struct mmsghdr __user *)a1,
                             a[2], a[3],
                             (struct __kernel_timespec __user *)a[4],
                             NULL);
            else
                err = __sys_recvmmsg(a0, (struct mmsghdr __user *)a1,
                             a[2], a[3], NULL,
                             (struct old_timespec32 __user *)a[4]);
            break;
        case SYS_ACCEPT4:
            err = __sys_accept4(a0, (struct sockaddr __user *)a1,
                        (int __user *)a[2], a[3]);
            break;
        default:
            err = -EINVAL;
            break;
        }
        return err;
    }

    在应用中调用socket()函数时,就会触发系统调用,跟socket相关的操作函数都会被映射到sys_socketcall的系统调用中(32位系统),在文件unistd_32.h中有其系统调用号表。

    例如查看sys_socket()的定义

      SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
      {
      return __sys_socket(family, type, protocol);
      }

    int
    __sys_socket(int family, int type, int protocol) { int retval; struct socket *sock; int flags; /* Check the SOCK_* constants for consistency. */ BUILD_BUG_ON(SOCK_CLOEXEC != O_CLOEXEC); BUILD_BUG_ON((SOCK_MAX | SOCK_TYPE_MASK) != SOCK_TYPE_MASK); BUILD_BUG_ON(SOCK_CLOEXEC & SOCK_TYPE_MASK); BUILD_BUG_ON(SOCK_NONBLOCK & SOCK_TYPE_MASK); flags = type & ~SOCK_TYPE_MASK; if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) return -EINVAL; type &= SOCK_TYPE_MASK; if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK)) flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK; retval = sock_create(family, type, protocol, &sock); if (retval < 0) return retval; return sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK)); }

    二、replyhi命令调用socket的过程

    查看lab3的main.c

    int main()
    {
        BringUpNetInterface();
        PrintMenuOS();
        SetPrompt("MenuOS>>");
        MenuConfig("version","MenuOS V1.0(Based on Linux 3.18.6)",NULL);
        MenuConfig("quit","Quit from MenuOS",Quit);
        MenuConfig("replyhi", "Reply hi TCP Service", StartReplyhi);
        MenuConfig("hello", "Hello TCP Client", Hello);
        ExecuteMenu();
    }

    键入replyhi调用StartReplyhi()函数,其调用Replyhi()函数。键入hello调用Hello()函数,查看其调用的函数定义,例如Replyhi()

    int Replyhi()
    {
        char szBuf[MAX_BUF_LEN] = "";
        char szReplyMsg[MAX_BUF_LEN] = "hi";
        InitializeService();
        while (1)
        {
            ServiceStart();
            RecvMsg(szBuf);
            SendMsg(szReplyMsg);
            ServiceStop();
        }
        ShutdownService();
        return 0;
    }

    查看在syswrapper.h中定义的SendMsg(),RecvMsg()函数

    #include <unistd.h> 
    #define
    RecvMsg(buf) ret = recv(newfd,buf,MAX_BUF_LEN,0); if(ret > 0) { printf("recv "%s" from %s:%d ", buf, (char*)inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); } #define SendMsg(buf) ret = send(newfd,buf,strlen(buf),0); if(ret > 0) { printf("send "hi" to %s:%d ", (char*)inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); }

    其加载了在文件unistd_32.h中的系统调用号表,调用了API函数send()和recv(),还有函数socket,bind,listen,accept,connect同理可询。

    可以通过在这些函数设置断点来查看系统调用的过程。

     三、使用gdb追踪socket的系统调用

    首先同上次试验,建立gdb与menuOS的通信,然后在menuOS执行replyhi与hello,通过在gdb设置断点来分析函数调用过程。

    在sys_socketcall设定断点

    (gdb)  b sys_socketcall

    然后键入c以继续执行查看调用情况 

     

     可以看到过程中多次调用了sys_socketcall

  • 相关阅读:
    真爱如血第七季/全集True Blood迅雷下载
    真爱如血第一季/全集True Blood迅雷下载
    女子监狱第四季/全集Orange Is the New Black迅雷下载
    女子监狱第一季/全集Orange Is the New Black迅雷下载
    护士当家第一至七季/全集Nurse Jackie迅雷下载
    都铎王朝第一至四季/全集The Tudors迅雷下载
    亿万第一至二季/全集Billions迅雷下载
    超感猎杀/超感八人组第一季至二季/全集Sense8迅雷下载
    双面女间谍第一至五季/全集Alias迅雷下载
    美国罪案故事第一季/全集American Crime Story迅雷下载
  • 原文地址:https://www.cnblogs.com/avriiil/p/12070562.html
Copyright © 2020-2023  润新知