socket工作機(jī)制

??首先談一下Socket 機(jī)制本身,socket為各種協(xié)議提供了統(tǒng)一接口的一種ipc機(jī)制伞访。在linux中掂骏,它由幾個部分組成。為了討論厚掷,先討論幾個數(shù)據(jù)結(jié)構(gòu)弟灼,如下所示:

struct net_proto_family {
         int             family;
         int             (*create)(struct socket *sock, int protocol);       
         short           authentication;
         short           encryption;
         short           encrypt_net;
         struct module   *owner;
};

??這個數(shù)據(jù)結(jié)構(gòu)定義在linux的kernel中级解,在文件src/include/linux/net.h中。其中family是用來標(biāo)示協(xié)議號的田绑。而那個create函數(shù)指針則表示用來創(chuàng)建socket時所對應(yīng)的create函數(shù)勤哗,owner則是這個協(xié)議的module結(jié)構(gòu)。同時掩驱,還定義一個協(xié)議數(shù):
????#define NPROTO 64
??再看一下socket的本身的定義:

struct socket {
         socket_state                     state;
         unsigned long                    flags;
         struct proto_ops                 *ops;
         struct fasync_struct             *fasync_list;
         struct file                      *file;
         struct sock                      *sk;
         wait_queue_head_t                wait;
         short                            type;
};

??ops指針?biāo)鶎?yīng)的是在這個socket上的一些操作俺陋,它的定義如下:

struct proto_ops {
         int             family;
         struct          module         *owner;
         int             (*release)      (struct socket *sock);
         int             (*bind)         (struct socket *sock,
                                          struct sockaddr *myaddr,
                                          int sockaddr_len);
         int             (*connect)      (struct socket *sock,
                                           struct sockaddr *vaddr,
                                           int sockaddr_len, int flags);
         int             (*socketpair)    (struct socket *sock1,
                                           struct socket *sock2);
         int             (*accept)    (struct socket *sock,
                                           struct socket *newsock, int flags);
         int             (*getname)   (struct socket *sock,
                                           struct sockaddr *addr,
                                           int *sockaddr_len, int peer);
         unsigned int   (*poll)       (struct file *file, struct socket *sock,
                                           struct poll_table_struct *wait);
         int             (*ioctl)     (struct socket *sock, unsigned int cmd,
                                           unsigned long arg);
         int             (*listen)    (struct socket *sock, int len);
         int             (*shutdown)  (struct socket *sock, int flags);
         int             (*setsockopt)(struct socket *sock, int level,
                                       int optname, char __user *optval,
                                       int optlen);
         int             (*getsockopt)(struct socket *sock, int level,
                                        int optname, char __user *optval,
                                          int __user *optlen);
         int             (*sendmsg)   (struct kiocb *iocb, struct socket *sock,
                                           struct msghdr *m, size_t total_len);
         int             (*recvmsg)   (struct kiocb *iocb, struct socket *sock,
                                           struct msghdr *m, size_t total_len,
                                           int flags);
         int             (*mmap)      (struct file *file, struct socket *sock,
                                              struct vm_area_struct * vma);
         ssize_t         (*sendpage)  (struct socket *sock, struct page *page,
                                           int offset, size_t size, int flags);
};

??從這個定義可以看出它定義了很多函數(shù)指針,也就是當(dāng)生成某個協(xié)議的socket時昙篙,這個協(xié)議所對應(yīng)的函數(shù)可以賦給這些函數(shù)指針。這樣協(xié)議的實(shí)現(xiàn)者和socket本身的實(shí)現(xiàn)機(jī)制就可以分開诱咏。
??在kernel中定義了一個靜態(tài)的全局?jǐn)?shù)組苔可,如下所示:

static struct net_proto_family * net_families[NPROTO];

??這個定義在kernel的socket.c中。當(dāng)linux系統(tǒng)啟動時袋狞,系統(tǒng)的init進(jìn)程會調(diào)用sock_init函數(shù)對這個數(shù)組初始化焚辅, 在init進(jìn)程中調(diào)用過程是:

/*start_kernel   =>  
 *rest_init   =>  
 *kernel_thread(init, NULL, CLONE_FS |CLONE_SIGHAND) => 
 *init  => 
 *do_basic_setup  => 
 *sock_init:
 */
for(int i = 0; i < NPROTO; i++)
    net_families[i]=NULL;  

??也就是每一個協(xié)議對應(yīng)這個數(shù)組的一項(xiàng)。同時在這個socket.c文件中還定義了一些socket注冊函數(shù):

int sock_register(struct net_proto_family *ops)
{
        int err;
        if (ops->family >= NPROTO) {
               printk(KERN_CRIT "protocol %d >= NPROTO(%d)\n", 
                      ops->family, NPROTO);
               return -ENOBUFS;
        }
        net_family_write_lock();
        err = -EEXIST;
        if (net_families[ops->family] == NULL) {
               net_families[ops->family]=ops;
               err = 0;
        }
        net_family_write_unlock();
        printk(KERN_INFO "NET: Registered protocol family %d\n",
               ops->family);
        return err;
}

??從這個代碼可以看出苟鸯,它最主要的工作就是在net_families數(shù)組所對應(yīng)的項(xiàng)中把協(xié)議所對應(yīng)的socket操作函數(shù)的net_proto_family結(jié)構(gòu)指針給賦上值同蜻,這樣當(dāng)給定某個協(xié)議的socket時,就能通過協(xié)議號在這個net_families數(shù)組中找對應(yīng)的項(xiàng)早处,進(jìn)而可以得到這個socket的實(shí)際的創(chuàng)建函數(shù)湾蔓,從而在需要生成一個新的這個協(xié)議的socket時調(diào)用用這個創(chuàng)建函數(shù)。
??那么這個socket注冊函數(shù)是在哪調(diào)用的呢砌梆?
??一般是在協(xié)議初始化被調(diào)用的默责。如tipc協(xié)議在linux中是作為一個module來實(shí)現(xiàn)的,那么在module的module_init(tipc_init);
??這個tipc_init調(diào)用關(guān)系如下:

/*
* tipc_init -> start_core -> start_core_base -> socket_init ->
* sock_register(&tipc_family_ops)咸包;
* 這個tipc_family_ops的定義如下:
*/
static struct net_proto_family tipc_family_ops = {
        .owner         = THIS_MODULE,
        .family        = AF_TIPC,
        .create        = tipc_create
};

??AF_TIPC就是TIPC對應(yīng)的協(xié)議標(biāo)示桃序,其值是30。而tipc_create函數(shù)就是tipc的socket的創(chuàng)建函數(shù)烂瘫。

static int tipc_create(struct socket *sock, int protocol)
{
        struct tipc_sock *tsock;
        struct tipc_port *port;
        struct sock *sk;
        u32 ref;
        struct task_struct *tsk;
        int size = (sizeof(tsock->comm) < sizeof(tsk->comm)) ?
                    sizeof(tsock->comm) : sizeof(tsk->comm);
 
        if ((protocol < 0) || (protocol >= MAX_TIPC_STACKS)) {
               warn("Invalid protocol number : %d, permitted range 0 - %d.\n",
                    protocol, MAX_TIPC_STACKS);
               return -EPROTONOSUPPORT;
        }
        if (protocol != 0) {
               int vres = handle_protocol(sock, protocol);
               return vres;
        }
 
        ref = tipc_createport_raw(0, &dispatch, &wakeupdispatch,
                                 TIPC_LOW_IMPORTANCE, 0);
        if (unlikely(!ref))
               return -ENOMEM;
 
        sock->state = SS_UNCONNECTED;
 
    switch (sock->type) {
        case SOCK_STREAM:
               sock->ops = &stream_ops;
               break;
        case SOCK_SEQPACKET:
               sock->ops = &packet_ops;
               break;
        case SOCK_DGRAM:
               tipc_set_portunreliable(ref, 1);
              
        case SOCK_RDM:
               tipc_set_portunreturnable(ref, 1);
               sock->ops = &msg_ops;
               sock->state = SS_READY;
               break;
        default:
               tipc_deleteport(ref);
               return -EPROTOTYPE;
        }
 
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
        sk = sk_alloc(AF_TIPC, GFP_KERNEL, &tipc_proto, 1);
#else
        sk = sk_alloc(AF_TIPC, GFP_KERNEL, 1, tipc_cache);
#endif
        if (!sk) {
               tipc_deleteport(ref);
               return -ENOMEM;
        }
 
        sock_init_data(sock, sk);
        init_waitqueue_head(sk->sk_sleep);
        sk->sk_rcvtimeo = 8 * HZ;  
 
        tsock = tipc_sk(sk);
        port = tipc_get_port(ref);
 
        tsock->p = port;
        port->usr_handle = tsock;
 
        init_MUTEX(&tsock->sem);
       
        memset(tsock->comm, 0, size);
        tsk = current;
        task_lock(tsk);
        tsock->pid = tsk->pid;
        memcpy(tsock->comm, tsk->comm, size);
        task_unlock(tsk);
       
        tsock->comm[size-1]=0;
 
        tsock->overload_hwm = 0;
 
        tsock->ovld_limit = tipc_persocket_overload;
 
        dbg("sock_create: %x\n",tsock);
 
        atomic_inc(&tipc_user_count);
 
        return 0;
}
??從這個函數(shù)的定義中可以看出媒熊,根據(jù)這個協(xié)議的不同的類型,如SOCK_STREAM還是SOCK_SEQPACKET坟比,這給生成socket的ops指針賦予不同的操作類型芦鳍,如下所示:
static struct proto_ops packet_ops = {
        .owner         = THIS_MODULE,
        .family        = AF_TIPC,
        .release       = release,
        .bind          = bind,
        .connect       = connect,
        .socketpair    = no_skpair,
        .accept        = accept,
        .getname       = get_name,
        .poll          = poll,
        .ioctl         = ioctl,
        .listen        = listen,
        .shutdown      = shutdown,
        .setsockopt    = setsockopt,
        .getsockopt    = getsockopt,
        .sendmsg       = send_packet,
        .recvmsg       = recv_msg,
        .mmap          = no_mmap,
        .sendpage      = no_sendpage
};
static struct proto_ops stream_ops = {
        .owner         = THIS_MODULE,
        .family        = AF_TIPC,
        .release       = release,
        .bind          = bind,
        .connect       = connect,
        .socketpair    = no_skpair,
        .accept        = accept,
        .getname       = get_name,
        .poll          = poll,
        .ioctl         = ioctl,
        .listen        = listen,
        .shutdown      = shutdown,
        .setsockopt    = setsockopt,
        .getsockopt    = getsockopt,
        .sendmsg       = send_stream,
        .recvmsg       = recv_stream,
        .mmap          = no_mmap,
        .sendpage      = no_sendpage
};

??以上所討論的都是linux內(nèi)核當(dāng)中的部分,但對于應(yīng)用程序來說葛账,使用socket編程時怜校,并不是直接與這些內(nèi)核當(dāng)中的接口打交道的。由于應(yīng)用程序運(yùn)行在用戶空間注竿,這這些接口是需要在內(nèi)核空間才可以調(diào)到茄茁。
??那么就有一個問題魂贬,應(yīng)用程序是如何調(diào)用到這些接口的呢?其中的奧秘就在于glibc這個庫裙顽。linux應(yīng)用程序是調(diào)用glibc中的socket函數(shù)來編程的付燥,在glibc中socket的函數(shù)只有一套,通過以上的這個機(jī)制它就可以對應(yīng)各種協(xié)議的socket函數(shù)愈犹。
??那么glibc中是如何調(diào)用到內(nèi)核中的函數(shù)的呢键科?我們先來看一下內(nèi)核socket.c這個文件,在這個文件中還定義了一個如下的函數(shù):

#ifdef __ARCH_WANT_SYS_SOCKETCALL
 
#define AL(x) ((x) * sizeof(unsigned long))
static unsigned char nargs[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3),
                                AL(3),AL(3),AL(4),AL(4),AL(4),AL(6),
                                AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)};
#undef AL
 
 
asmlinkage long sys_socketcall(int call, unsigned long __user *args)
{
        unsigned long a[6];
        unsigned long a0,a1;
        int err;
 
        if(call<1||call>SYS_RECVMSG)
               return -EINVAL;
 
        if (copy_from_user(a, args, nargs[call]))
               return -EFAULT;
 
        err = audit_socketcall(nargs[call]/sizeof(unsigned long), a);
        if (err)
               return err;
 
        a0=a[0];
        a1=a[1];
 
        trace_socket_call(call, a0);
       
        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_accept(a0,(struct sockaddr __user *)a1, (int __user *)a[2]);
                       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_send(a0, (void __user *)a1, a[2], a[3]);
                       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_recv(a0, (void __user *)a1, a[2], a[3]);
                       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 msghdr __user *) a1, a[2]);
                       break;
               case SYS_RECVMSG:
                       err = sys_recvmsg(a0, (struct msghdr __user *) a1, a[2]);
                       break;
               default:
                       err = -EINVAL;
                       break;
        }
        return err;
}
 
#endif

??這個sys_socketcall是一個系統(tǒng)調(diào)用漩怎,所有的glibc中的socket 函數(shù)都是通過這個系統(tǒng)調(diào)用進(jìn)入到內(nèi)核空間的勋颖。我們來看accept的調(diào)用。glibc中accept的調(diào)用在:sysdeps\unix\sysv\linux\accept.S文件中:

//glibc-2.0.111\sysdeps\unix\sysv\linux\accept.S
#define  socket   accept
#define  __socket __libc_accept
#define  NARGS    3
#define  NEED_CANCELLATION
#include <socket.S>
libc_hidden_def (accept)

??這段與socket.S是accept()從用戶態(tài)進(jìn)入內(nèi)核態(tài)的關(guān)鍵代碼勋锤。accept.S中將accept定義為socket饭玲,__socket定義為__libc_accpet,NARGS定義為3叁执,表示調(diào)用參數(shù)有3個茄厘。接下來包含了socket.S文件,如下:

The socket-oriented system calls are handled unusally in Linux.
They are all gated through the single `socketcall' system call number.
`socketcall' takes two arguments: the first is the subcode, specifying
which socket function is being called; and the second is a pointer to
the arguments to the specific function.
The .S files for the other calls just #define socket and #include this.  

翻譯成中文的大概意思是:
??socket系列的系統(tǒng)函數(shù)經(jīng)常被調(diào)用谈宛。他們都通過單一的一個socketcall系統(tǒng)調(diào)用號(進(jìn)行調(diào)用)次哈。Socketcall有兩個參數(shù):第一個是子調(diào)用碼,指定了哪一個socket函數(shù)被調(diào)用吆录;第二個參數(shù)是一個指向被調(diào)用的socket函數(shù)所需參數(shù)的指針窑滞。其他的(socket系列的)函數(shù)的.S文件只需要#define socket 為某個值和#include 這個文件(指此socket.S)即可。
??在socket.S中進(jìn)行了進(jìn)一步的調(diào)用恢筝,socket從用戶態(tài)進(jìn)行相應(yīng)參數(shù)的設(shè)置葛假,然后使用int指令自陷,調(diào)用操作系統(tǒng)提供的中斷服務(wù)程序滋恬,在內(nèi)核態(tài)執(zhí)行相應(yīng)的系統(tǒng)服務(wù)聊训,我們將整個函數(shù)的代碼粘貼進(jìn)來,在具體的語句上進(jìn)行注釋解釋:

 1 // glibc-2.0.111\sysdeps\unix\sysv\linux\i386\socket.S
 2 #include <sysdep.h>
 3 #include <socketcall.h>
 4 // 定義了P(a,b)與P2(a,b)兩個宏恢氯,他們的作用都是將a與b連接到一起带斑。
 5 #define P(a, b) P2(a, b)
 6 #define P2(a, b) a##b
 7 
 8     .text
 9 
10 #ifndef __socket
11 #ifndef NO_WEAK_ALIAS
12 #define __socket P(__,socket)     
13 #else
14 #define __socket socket
15 #endif
16 #endif
17 
18 .globl __socket
19 ENTRY (__socket)   //這里開始進(jìn)行函數(shù)的處理
20 
21 
22     /* 保存ebx的值  */
23     movl %ebx, %edx
24 
25     // SYS_ify宏在sysdep.h中定義。一會兒詳細(xì)了解它的作用
26     // 下面一條語句的作用是將socketcall的調(diào)用號存入寄存器eax
27     movl $SYS_ify(socketcall), %eax    /* System call number in %eax.  */
28 
29     /* 子調(diào)用號放入ebx中勋拟,關(guān)于下面一條語句的將在下面有詳細(xì)解釋  */
30     movl $P(SOCKOP_socket), %ebx    /* Subcode is first arg to syscall.  */
31     /* 指向調(diào)用參數(shù)的指針放入ecx中  */
32     lea 4(%esp), %ecx        /* Address of args is 2nd arg.  */
33 
34         /* 0x80中斷勋磕,自陷進(jìn)入內(nèi)核態(tài) */
35     int $0x80
36 
37     /* 恢復(fù)ebx寄存器的值 */
38     movl %edx, %ebx
39 
40     /* eax是返回值,如果<0則表示調(diào)用出錯敢靡,就跳到錯誤處理的代碼中去  */
41     cmpl $-125, %eax
42     jae SYSCALL_ERROR_LABEL
43 
44     /* 成功的話就返回相應(yīng)的返回值  */
45 L(pseudo_end):
46     ret
47 
48 PSEUDO_END (__socket)
49 
50 #ifndef NO_WEAK_ALIAS
51 weak_alias (__socket, socket)
52 #endif

??我們首先看movl $SYS_ify(socketcall), %eax這一條語句挂滓。SYS_ify在sysdep.h中定義,但是有兩個不同文件夾下的sysdep.h文件啸胧。

(1)

??按照文件層次來講赶站,應(yīng)該是按照如下的代碼進(jìn)行:

1 // glibc-2.0.111\sysdeps\unix\sysv\linux\i386\sysdep.h
2 .....
3 #undef SYS_ify
4 #define SYS_ify(syscall_name)    __NR_##syscall_name
5 .....

??在這段代碼之前有一段注釋:

For Linux we can use the system call table in the header file 
/usr/include/asm/unistd.hof the kernel.  But these symbols do not follow the 
SYS_* syntax so we have to redefine the `SYS_ify' macro here. 

對于Linux系統(tǒng)幔虏,我們可以使用在/usr/include/asm/unistd.h頭文件中的內(nèi)核系統(tǒng)調(diào)用表。
但是這些符號并不是以SYS_符號為前綴的贝椿,所以這里我們必須重定義SYS_ify宏想括。
可以看到,通過SYS_ify(socketcall)烙博,我們得到了__NR_socketcall瑟蜈。
(2)

??按照另外一本書上所講的,在下列位置中存在另外一套代碼:

1 // glibc-2.0.111\sysdeps\unix\sysdep.h
2 ……
3 #ifdef __STDC__
4 #define SYS_ify(syscall_name) SYS_##syscall_name
5 #else
6 #define SYS_ify(syscall_name) SYS_/**/syscall_name
7 #endif
8 ……

??如果是經(jīng)由這段代碼的處理渣窜,那么我們將得到SYS_socketcall铺根,那么這又是一個什么呢?我們查看源代碼是看不到的乔宿。而在實(shí)際的操作系統(tǒng)(筆者所使用的是Fedora 14)中位迂,/usr/include /bits/syscall.h中則有相應(yīng)的答案,這個文件是libc在構(gòu)建時候根據(jù)具體的操作系統(tǒng)而生成的予颤。在其中,會有:

 1 #ifndef _SYSCALL_H
 2 # error "Never use <bits/syscall.h> directly; include <sys/syscall.h> instead."
 3 #endif
 4 
 5 #define SYS__llseek __NR__llseek
 6 #define SYS__newselect __NR__newselect
 7 #define SYS__sysctl __NR__sysctl
 8 #define SYS_access __NR_access
 9 #define SYS_acct __NR_acct
10 ……
11 #define SYS_socketcall __NR_socketcall
12 ……

可以看到冬阳,通過這一部分的處理之后蛤虐,最后依然會得到__NR_socketcall。
??了解Linux系統(tǒng)的人都知道肝陪,在/linux/include/linux/unistd.h中驳庭,我們可以看到,這些內(nèi)容:

 1 // linux/include/linux/unistd.h
 2 ……
 3 #define __NR_setup          0    /* used only by init, to get system going */
 4 #define __NR_exit          1
 5 #define __NR_fork          2
 6 #define __NR_read          3
 7 #define __NR_write          4
 8 ……
 9 #define __NR_socketcall        102
10 ……

我們可以看到氯窍,__NR_socketcall被定義為102饲常,上面一行的代碼即是將eax的值賦成102,即此系統(tǒng)調(diào)用的調(diào)用號狼讨。下面我們看movl $P(SOCKOP_socket), %ebx這一句贝淤。在socketcall.h中有相應(yīng)的定義:

 1 // glibc-2.0.111\sysdeps\unix\sysv\linux\socketcall.h
 2 ……
 3 #define SOCKOP_socket        1
 4 #define SOCKOP_bind        2
 5 #define SOCKOP_connect        3
 6 #define SOCKOP_listen        4
 7 #define SOCKOP_accept        5
 8 #define SOCKOP_getsockname    6
 9 #define SOCKOP_getpeername    7
10 #define SOCKOP_socketpair    8
11 #define SOCKOP_send        9
12 #define SOCKOP_recv        10
13 #define SOCKOP_sendto        11
14 #define SOCKOP_recvfrom        12
15 #define SOCKOP_shutdown        13
16 #define SOCKOP_setsockopt    14
17 #define SOCKOP_getsockopt    15
18 #define SOCKOP_sendmsg        16
19 #define SOCKOP_recvmsg        17
20 ……

??這一句的意思就是將相應(yīng)的操作碼賦予ebx,此例中是5政供。下面我們進(jìn)入操作系統(tǒng)中的代碼進(jìn)行分析播聪,在entry.S中,有一段中斷處理函數(shù):

 1 // linux/arch/i386/kernel/entry.S
 2 _system_call:
 3 // 保存eax的值
 4     pushl %eax            # save orig_eax
 5 // 保存所有寄存器的值
 6     SAVE_ALL
 7     movl $-ENOSYS,EAX(%esp)
 8 // 比較eax中的調(diào)用號是否超過了限定的數(shù)值布隔,NR_syscalls离陶,默認(rèn)是256。
 9     cmpl $(NR_syscalls),%eax  # compare whether eax>NR_syscalls
10     jae ret_from_sys_call
11 //從系統(tǒng)調(diào)用表中找到對應(yīng)的入口地址衅檀,放入eax中
12     movl _sys_call_table(,%eax,4),%eax
13     testl %eax,%eax
14     je ret_from_sys_call
15 // 子調(diào)用號放入ebx中
16     movl _current,%ebx
17     andl $~CF_MASK,EFLAGS(%esp)    # clear carry - assume no errors
18     movl $0,errno(%ebx)
19     movl %db6,%edx
20     movl %edx,dbgreg6(%ebx)  # save current hardware debugging status
21     testb $0x20,flags(%ebx)        # PF_TRACESYS
22     jne 1f
23 // 進(jìn)行系統(tǒng)調(diào)用
24     call *%eax
25     movl %eax,EAX(%esp)        # save the return value
26     movl errno(%ebx),%edx
27     negl %edx
28     je ret_from_sys_call
29     movl %edx,EAX(%esp)
30     orl $(CF_MASK),EFLAGS(%esp)    # set carry to indicate error
31     jmp ret_from_sys_call

??具體的語句的作用已經(jīng)在代碼中進(jìn)行了標(biāo)注招刨。我們接下來可以查看處理socket調(diào)用的系統(tǒng)函數(shù)socket.c:

 1 // linux/net/socket.c
 2 ……
 3 asmlinkage int sys_socketcall(int call, unsigned long *args)
 4  {
 5     int er;
 6     switch(call) 
 7     {
 8         case SYS_SOCKET:
 9             er=verify_area(VERIFY_READ, args, 3 * sizeof(long));
10             if(er)
11                 return er;
12             return(sock_socket(get_fs_long(args+0),
13                 get_fs_long(args+1),
14                 get_fs_long(args+2)));
15 ……
16         case SYS_ACCEPT:
17             er=verify_area(VERIFY_READ, args, 3 * sizeof(long));
18             if(er)
19                 return er;
20             return(sock_accept(get_fs_long(args+0),
21                 (struct sockaddr *)get_fs_long(args+1),
22                 (int *)get_fs_long(args+2)));
23 ……

??這個sys_socketcall函數(shù)是socket系列函數(shù)的分發(fā)函數(shù),根據(jù)具體調(diào)用號哀军,調(diào)用不同的處理函數(shù)進(jìn)行處理沉眶,至此打却,我們看到了整個從應(yīng)用層socket函數(shù)到BSDsocket的層的傳遞過程,加深了我們對于此過程的了解沦寂。

本文非原創(chuàng)

參考文獻(xiàn):

http://blog.sina.com.cn/s/blog_605507340101cwaf.html
https://www.xuebuyuan.com/910647.html
https://segmentfault.com/a/1190000008926093

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末学密,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子传藏,更是在濱河造成了極大的恐慌腻暮,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件毯侦,死亡現(xiàn)場離奇詭異哭靖,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)侈离,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門试幽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人卦碾,你說我怎么就攤上這事祷愉〗淀铮” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長寄纵。 經(jīng)常有香客問我蹲姐,道長丹泉,這世上最難降的妖魔是什么骂蓖? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮叉弦,結(jié)果婚禮上丐一,老公的妹妹穿的比我還像新娘。我一直安慰自己淹冰,他們只是感情好库车,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著樱拴,像睡著了一般凝颇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上疹鳄,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天拧略,我揣著相機(jī)與錄音,去河邊找鬼瘪弓。 笑死垫蛆,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播袱饭,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼川无,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了虑乖?” 一聲冷哼從身側(cè)響起懦趋,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎疹味,沒想到半個月后仅叫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡糙捺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年诫咱,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片洪灯。...
    茶點(diǎn)故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡坎缭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出签钩,到底是詐尸還是另有隱情掏呼,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布铅檩,位于F島的核電站憎夷,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏柠并。R本人自食惡果不足惜岭接,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一富拗、第九天 我趴在偏房一處隱蔽的房頂上張望臼予。 院中可真熱鬧,春花似錦啃沪、人聲如沸粘拾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缰雇。三九已至,卻和暖如春追驴,著一層夾襖步出監(jiān)牢的瞬間械哟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工殿雪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留暇咆,地道東北人。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像爸业,于是被迫代替她去往敵國和親其骄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容