青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

大龍的博客

常用鏈接

統(tǒng)計

最新評論

Linux TCP/IP協(xié)議棧之Socket的實現(xiàn)分析(一 套接字的創(chuàng)建) --- 轉(zhuǎn)

Socket并不是TCP/IP協(xié)議的一部份,從廣義上來講,socket是Unix/Linux抽像的進程間通訊的一種方法
網(wǎng)絡(luò)socket通訊僅僅是其若干協(xié)議中的一類,而tcp/ip又是網(wǎng)絡(luò)協(xié)議各類中的一種
從tcp/ip的角度看socket,它更多地體現(xiàn)了用戶API與協(xié)議棧的一個中間層接口層
用戶通過調(diào)用socket API將報文遞交給協(xié)議棧,或者從協(xié)議棧中接收報文

系統(tǒng)總?cè)肟?/strong>
Linux內(nèi)核為所有的與socket有關(guān)操作的API,提供了一個統(tǒng)一的系統(tǒng)調(diào)用入口,其代碼在net/socket.c中
asmlinkage long sys_socketcall(int call, unsigned long __user *args)
{
    ...
    /* copy_from_user should be SMP safe. */
    if (copy_from_user(a, args, nargs[call]))
        return -EFAULT;

    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_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;
}
首先調(diào)用 copy_from_user將用戶態(tài)參數(shù)拷貝至數(shù)組a,但是問題在于每個被調(diào)用的 API 的參數(shù)不盡相同,
那么每次拷貝的字節(jié)在小如何判斷.來看其第三個參數(shù) nargs[call],其中 call 是操作碼,后面有個大大的
switch...case 就是判斷它。對應(yīng)的操作碼定義在 include/linux/net.h
#define SYS_SOCKET                     1                /* sys_socket(2)                */
#define SYS_BIND                         2                /* sys_bind(2)                        */
#define SYS_CONNECT                  3                /* sys_connect(2)                */
#define SYS_LISTEN                       4                /* sys_listen(2)                */
#define SYS_ACCEPT                     5                /* sys_accept(2)                */
#define SYS_GETSOCKNAME        6                /* sys_getsockname(2)                */
#define SYS_GETPEERNAME         7                /* sys_getpeername(2)                */
#define SYS_SOCKETPAIR             8                /* sys_socketpair(2)                */
#define SYS_SEND                          9                /* sys_send(2)                        */
#define SYS_RECV                         10                /* sys_recv(2)                        */
#define SYS_SENDTO                    11                /* sys_sendto(2)                */
#define SYS_RECVFROM               12                /* sys_recvfrom(2)                */
#define SYS_SHUTDOWN              13                /* sys_shutdown(2)                */
#define SYS_SETSOCKOPT           14                /* sys_setsockopt(2)                */
#define SYS_GETSOCKOPT          15                /* sys_getsockopt(2)                */
#define SYS_SENDMSG                 16                /* sys_sendmsg(2)                */
#define SYS_RECVMSG                 17                /* sys_recvmsg(2)                */
而數(shù)組nargs則根據(jù)操作碼的不同,計算對應(yīng)的參數(shù)的空間大小:
/* Argument list sizes for 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
當(dāng)拷貝完成參數(shù)后,就進入一個switch...case...判斷操作碼,跳轉(zhuǎn)至對應(yīng)的系統(tǒng)接口.

sys_socket函數(shù)
操作碼 SYS_SOCKET 是由 sys_socket()實現(xiàn)的:
1239 asmlinkage long sys_socket(int family, int type, int protocol)
1240 {
1241     int retval;
1242     struct socket *sock;
1243
1244     retval = sock_create(family, type, protocol, &sock);
1245     if (retval < 0)
1246         goto out;
1247
1248     retval = sock_map_fd(sock);
1249     if (retval < 0)
1250         goto out_release;
1251
1252 out:
1253     /* It may be already another descriptor 8) Not kernel problem. */
1254     return retval;
1255
1256 out_release:
1257     sock_release(sock);
1258     return retval;
1259 }
在分析這段代碼之前, 首先來看創(chuàng)建一個Socket, 對內(nèi)核而言,究竟意味著什么?究竟需要內(nèi)核干什么事?
當(dāng)用戶空間要創(chuàng)建一個 socke 接口時,會調(diào)用 API 函數(shù)
int socket(int domain, int type, int protocol)
其三個參數(shù)分別表示協(xié)議族,協(xié)議類型(面向連接或無連接)以及協(xié)議

對于用戶態(tài)而言, 一個Socket, 就是一個特殊的已經(jīng)打開的文件,為了對socket抽像出文件的概念,
內(nèi)核中為socket定義了一個專門的文件系統(tǒng)類型sockfs.
 344 static struct vfsmount *sock_mnt __read_mostly;
 345
 346 static struct file_system_type sock_fs_type = {                                                                        
 347     .name =     "sockfs",
 348     .get_sb =   sockfs_get_sb,
 349     .kill_sb =    kill_anon_super,
 350 };
在模塊初始化的時候,安裝該文件系統(tǒng): 
void __init sock_init(void)
{
        ……
        register_filesystem(&sock_fs_type);
        sock_mnt = kern_mount(&sock_fs_type);        
}
稍后還要回來繼續(xù)分析安裝中的一點細節(jié)

有了文件系統(tǒng)后,對內(nèi)核而言,創(chuàng)建一個socket,就是在sockfs文件系統(tǒng)中創(chuàng)建一個文件節(jié)點(inode),并建立起為了實現(xiàn)
socket功能所需的一整套數(shù)據(jù)結(jié)構(gòu),包括struct inode和struct socket結(jié)構(gòu).
struct socket結(jié)構(gòu)在內(nèi)核中,就代表了一個"Socket",當(dāng)一個struct socket數(shù)據(jù)結(jié)構(gòu)被分配空間后,再將其與一個已打開
的文件“建立映射關(guān)系”.這樣,用戶態(tài)就可以用抽像的文件的概念來操作socket了
——當(dāng)然由于網(wǎng)絡(luò)的特殊性,至少就目前而言,這種抽像,并不如其它模塊的抽像那么完美.

文件系統(tǒng)struct vfsmount中有一個成員指針mnt_sb指向該文件系統(tǒng)的超級塊,而超級塊結(jié)構(gòu)struct super_lock
有一個重要的成員s_op指向了超級塊的操作函數(shù)表,其中有函數(shù)指針alloc_inode()即為在給定的超級塊下創(chuàng)建并初始化
一個新的索引節(jié)點對像. 也就是調(diào)用:
sock_mnt->mnt_sb->s_op->alloc_inode(sock_mnt->mnt_sb);
當(dāng)然,連同相關(guān)的處理細節(jié)一起,這一操作被層層封裝至一個上層函數(shù)new_inode()

那如何分配一個struct socket結(jié)構(gòu)呢?
如前所述,一個socket總是與一個inode 密切相關(guān)的.當(dāng)然,在 inode 中設(shè)置一個socket成員是完全可行的,
但是這貌似浪費了空間——畢竟更多的文件系統(tǒng)沒有socket這個東東.
所以,內(nèi)核引入了另一個socket_alloc結(jié)構(gòu)
struct socket_alloc {
        struct socket socket;
        struct inode vfs_inode;
};
顯而易見,該結(jié)構(gòu)實現(xiàn)了inode和socket的封裝.已知一個inode可以通過宏SOCKET_I來獲取
與之對應(yīng)的 socket:
sock = SOCKET_I(inode);
static inline struct socket *SOCKET_I(struct inode *inode)
{
        return &container_of(inode, struct socket_alloc, vfs_inode)->socket;
}
但是這樣做也同時意味著在分配一個inode后,必須再分配一個socket_alloc結(jié)構(gòu),并實現(xiàn)對應(yīng)的封裝.
否則container_of又能到哪兒去找到socket呢?
現(xiàn)在來簡要地看一個這個流程——這是文件系統(tǒng)安裝中的一個重要步驟

881 struct vfsmount *kern_mount(struct file_system_type *type)
882 {
883     return vfs_kern_mount(type, 0, type->name, NULL);
884 }

817 struct vfsmount *
818 vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
819 {
820     struct vfsmount *mnt;
821     char *secdata = NULL;
822     int error;
           ....
828     mnt = alloc_vfsmnt(name);
829     if (!mnt)
830         goto out;
841     ....
842     error = type->get_sb(type, flags, name, data, mnt);
843     if (error < 0)
844         goto out_free_secdata;
849
850     mnt->mnt_mountpoint = mnt->mnt_root;
851     mnt->mnt_parent = mnt;
852     up_write(&mnt->mnt_sb->s_umount);
853     free_secdata(secdata);
854     return mnt;
855     .....
865 }
申請文件系統(tǒng)mnt結(jié)構(gòu), 調(diào)用之前注冊的sock_fs_type的get_sb成員函數(shù)指針, 獲取相應(yīng)的超級塊sb.
并將mnt->mnt_sb指向sock_fs_type中的超級塊

337 static int sockfs_get_sb(struct file_system_type *fs_type,
338     int flags, const char *dev_name, void *data, struct vfsmount *mnt)
339 {
340     return get_sb_pseudo(fs_type, "socket:", &sockfs_ops, SOCKFS_MAGIC,                                                
341                  mnt);
342 }
注意其第三個參數(shù) sockfs_ops,它封裝了 sockfs 的功能函數(shù)表
331 static struct super_operations sockfs_ops = {
332     .alloc_inode =  sock_alloc_inode,
333     .destroy_inode =sock_destroy_inode,
334     .statfs =   simple_statfs,
335 };

struct super_block *
get_sb_pseudo(struct file_system_type *fs_type, char *name,
        struct super_operations *ops, unsigned long magic)
{
        struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL);
                ……
        s->s_op = ops ? ops : &default_ops;
}
這里就是先獲取/分配一個超級塊,然后初始化超級塊的各成員,包括s_op,它封裝了對應(yīng)的功能函數(shù)表.
s_op自然就指向了sockfs_ops,那前面提到的new_inode()函數(shù)分配inode時調(diào)用的
sock_mnt->mnt_sb->s_op->alloc_inode(sock_mnt->mnt_sb);
這個alloc_inode函數(shù)指針也就是sockfs_ops的sock_alloc_inode()函數(shù)——轉(zhuǎn)了一大圈,終于指到它了.

來看看sock_alloc_inode是如何分配一個inode節(jié)點的
 283 static kmem_cache_t * sock_inode_cachep __read_mostly;
 284
 285 static struct inode *sock_alloc_inode(struct super_block *sb)
 286 {
 287     struct socket_alloc *ei;
 288     ei = (struct socket_alloc *)kmem_cache_alloc(sock_inode_cachep, SLAB_KERNEL);                                      
 289     if (!ei)
 290         return NULL;
 291     init_waitqueue_head(&ei->socket.wait);
 292    
 293     ei->socket.fasync_list = NULL;
 294     ei->socket.state = SS_UNCONNECTED;
 295     ei->socket.flags = 0;
 296     ei->socket.ops = NULL;
 297     ei->socket.sk = NULL;
 298     ei->socket.file = NULL;
 299     ei->socket.flags = 0;
 300
 301     return &ei->vfs_inode;
 302 }
函數(shù)先分配了一個用于封裝socket和inode的 ei,然后在高速緩存中為之申請了一塊空間.
這樣inode和socket就同時都被分配了,接下來初始化socket的各個成員,這些成員在后面都會一一提到
 96 /**
 97  *  struct socket - general BSD socket
 98  *  @state: socket state (%SS_CONNECTED, etc)
 99  *  @flags: socket flags (%SOCK_ASYNC_NOSPACE, etc)
100  *  @ops: protocol specific socket operations
101  *  @fasync_list: Asynchronous wake up list
102  *  @file: File back pointer for gc
103  *  @sk: internal networking protocol agnostic socket representation
104  *  @wait: wait queue for several uses
105  *  @type: socket type (%SOCK_STREAM, etc)
106  */
107 struct socket {
108     socket_state        state;
109     unsigned long       flags;
110     const struct proto_ops  *ops;
111     struct fasync_struct    *fasync_list;
112     struct file     *file;
113     struct sock     *sk;                                                                                                
114     wait_queue_head_t   wait;
115     short           type;
116 };
至目前為止,分配inode,socket以及兩者如何關(guān)聯(lián),都已一一分析了.
最后一個關(guān)鍵問題,就是如何把socket與一個已打開的文件,建立映射關(guān)系.

在內(nèi)核中,用struct file結(jié)構(gòu)描述一個已經(jīng)打開的文件,指向該結(jié)構(gòu)的指針內(nèi)核中通常用file或filp來描述.
我們知道,內(nèi)核中可以通過全局項current來獲得當(dāng)前進程,它是一個struct task_struct類型的指針.
tastk_struct有一個成員:
struct files_struct *files;指向一個已打開的文件.
當(dāng)然,由于一個進程可能打開多個文件,所以,struct files_struct 結(jié)構(gòu)有
struct file *fd_array[NR_OPEN_DEFAULT]成員,
這是個數(shù)組,以文件描述符為下標(biāo),即current->files->fd[fd],可以找到與當(dāng)前進程指定文件描述符的文件

有了這些基礎(chǔ),如果要把一個socket與一個已打開的文件建立映射,首先要做的就是為socket分配一個struct file,
并申請分配一個相應(yīng)的文件描述符fd. 因為socket并不支持open方法,所以不能期望用戶界面通過調(diào)用open() API
來分配一個struct file,而是通過調(diào)用get_empty_filp來獲取
struct file *file = get_empty_filp()
fd = get_unused_fd();獲取一個空間的文件描述符
然后讓current的files指針的fd數(shù)組的fd索引項指向該file
void fastcall fd_install(unsigned int fd, struct file * file)
{
        struct files_struct *files = current->files;
        spin_lock(&files->file_lock);
        if (unlikely(files->fd[fd] != NULL))
                BUG();
        files->fd[fd] = file;
        spin_unlock(&files->file_lock);
}
做到這一步,有了一個文件描述符fd和一個打開的文件file,它們與當(dāng)前進程相連,但是好像與創(chuàng)建的socket并無任何瓜葛.
要做的映射還是沒有進展,struct file或者文件描述述fd或current都沒有任何能夠與 inode或者是socket相關(guān)的東東
這需要一個中間的橋梁,目錄項:struct dentry結(jié)構(gòu)

因為一個文件都有與其對應(yīng)的目錄項:
struct file {
        struct list_head        f_list;
        struct dentry          *f_dentry;
        ……
而一個目錄項:
struct dentry {
        ……
        struct inode *d_inode;                /* Where the name belongs to - NULL is negative */
d_inode 成員指向了與之對應(yīng)的 inode節(jié)點

之前已經(jīng)創(chuàng)建了一個inode節(jié)點和與之對應(yīng)的 socket. 所以現(xiàn)在要做的就是:
“先為當(dāng)前文件分配一個對應(yīng)的目錄項,再將已創(chuàng)建的 inode節(jié)點安裝至該目錄項”
這樣一個完成的映射關(guān)系:
進程,文件描述符,打開文件,目錄項,inode節(jié)點,socket就完整地串起來了
 
基本要分析的一些前導(dǎo)的東東都一一羅列了,雖然已盡量避免陷入文件系統(tǒng)的細節(jié)分析,但是還是不可避免地進入其中,
因為它們關(guān)系實現(xiàn)太緊密了,現(xiàn)在可以來看套接字的創(chuàng)建過程了
1239 asmlinkage long sys_socket(int family, int type, int protocol)
1240 {
1241     int retval;
1242     struct socket *sock;
1243
1244     retval = sock_create(family, type, protocol, &sock);                                                               
1245     if (retval < 0)
1246         goto out;
1247
1248     retval = sock_map_fd(sock);
1249     if (retval < 0)
1250         goto out_release;
1251
1252 out:
1253     /* It may be already another descriptor 8) Not kernel problem. */
1254     return retval;
1255
1256 out_release:
1257     sock_release(sock);
1258     return retval;
1259 }
1229 int sock_create(int family, int type, int protocol, struct socket **res)
1230 {
1231     return __sock_create(family, type, protocol, res, 0);
1232 }

AF_INET協(xié)議簇的協(xié)議封裝
接下來,函數(shù)調(diào)用之前已經(jīng)注冊的inet_family_ops的函數(shù)指針create,也就是inet_create()函數(shù).
前面可以說一個通用的socket已經(jīng)創(chuàng)建好了,這里要完成與協(xié)議本身相關(guān)的一些創(chuàng)建socket的工作.
這一部份的工作比較復(fù)雜,還是先來看看af_inet.c中的模塊初始化時候,做了哪些與此相關(guān)的工作.

要引入的第一個數(shù)據(jù)結(jié)構(gòu)是struct inet_protosw,它封裝了一個協(xié)議類型(如 SOCK_STREAM,SOCK_DGRAM等)
與IP協(xié)議中對應(yīng)的傳輸層協(xié)議.
 68 /* This is used to register socket interfaces for IP protocols.  */                                                     
 69 struct inet_protosw {     
 70     struct list_head list;
 71
 72     /* These two fields form the lookup key.  */
 73     unsigned short   type;     /* This is the 2nd argument to socket(2). */
 74     int      protocol; /* This is the L4 protocol number.  */                                                           
 75
 76     struct proto     *prot;
 77     const struct proto_ops *ops;                                                                                        
 78  
 79     int                capability;   /* Which (if any) capability do we need to use this socket interface*/            
 83     char             no_check;   /* checksum on rcv/xmit/none? */
 84     unsigned char    flags;    /* See INET_PROTOSW_* below.  */                                                       
 85 };
type是協(xié)議類型,對于 ipv4 而言就是SOCK_STREAM,SOCK_DGRAM或者是SOCK_RAW之一.
protocol是傳輸層的協(xié)議號,prot用于描述一個具體的傳輸層協(xié)議,而ops指向?qū)?yīng)的當(dāng)前協(xié)議類型的操作函數(shù)集
針對不同的協(xié)議類型,定義了不同的 ops:
791 const struct proto_ops inet_stream_ops = {
 792     .family        = PF_INET,
 793     .owner         = THIS_MODULE,
 794     .release       = inet_release,
 795     .bind          = inet_bind,
 796     .connect       = inet_stream_connect,
 797     .socketpair    = sock_no_socketpair,
 798     .accept        = inet_accept,
 799     .getname       = inet_getname,
 800     .poll          = tcp_poll,
 801     .ioctl         = inet_ioctl,
 802     .listen        = inet_listen,
 803     .shutdown      = inet_shutdown,
 804     .setsockopt    = sock_common_setsockopt,
 805     .getsockopt    = sock_common_getsockopt,
 806     .sendmsg       = inet_sendmsg,
 807     .recvmsg       = sock_common_recvmsg,
 808     .mmap          = sock_no_mmap,
 809     .sendpage      = tcp_sendpage,
 810 #ifdef CONFIG_COMPAT
 811     .compat_setsockopt = compat_sock_common_setsockopt,
 812     .compat_getsockopt = compat_sock_common_getsockopt,
 813 #endif
 814 };
 815
 816 const struct proto_ops inet_dgram_ops = {
 817     .family        = PF_INET,
 818     .owner         = THIS_MODULE,
 819     .release       = inet_release,
 820     .bind          = inet_bind,
 821     .connect       = inet_dgram_connect,
 822     .socketpair    = sock_no_socketpair,
 823     .accept        = sock_no_accept,
 824     .getname       = inet_getname,
 825     .poll          = udp_poll,
 826     .ioctl         = inet_ioctl,
 827     .listen        = sock_no_listen,
828     .shutdown      = inet_shutdown,
 829     .setsockopt    = sock_common_setsockopt,
 830     .getsockopt    = sock_common_getsockopt,
 831     .sendmsg       = inet_sendmsg,
 832     .recvmsg       = sock_common_recvmsg,
 833     .mmap          = sock_no_mmap,
 834     .sendpage      = inet_sendpage,
 835 #ifdef CONFIG_COMPAT
 836     .compat_setsockopt = compat_sock_common_setsockopt,
 837     .compat_getsockopt = compat_sock_common_getsockopt,
 838 #endif
 839 };
 840
 841 /*
 842  * For SOCK_RAW sockets; should be the same as inet_dgram_ops but without
 843  * udp_poll
 844  */
845 static const struct proto_ops inet_sockraw_ops = {
 846     .family        = PF_INET,
 847     .owner         = THIS_MODULE,
 848     .release       = inet_release,
 849     .bind          = inet_bind,
 850     .connect       = inet_dgram_connect,
 851     .socketpair    = sock_no_socketpair,
 852     .accept        = sock_no_accept,
 853     .getname       = inet_getname,
 854     .poll          = datagram_poll,
 855     .ioctl         = inet_ioctl,                                                                                       
 856     .listen        = sock_no_listen,
 857     .shutdown      = inet_shutdown,
 858     .setsockopt    = sock_common_setsockopt,
 859     .getsockopt    = sock_common_getsockopt,
 860     .sendmsg       = inet_sendmsg,
 861     .recvmsg       = sock_common_recvmsg,
 862     .mmap          = sock_no_mmap,
 863     .sendpage      = inet_sendpage,
 864 #ifdef CONFIG_COMPAT
 865     .compat_setsockopt = compat_sock_common_setsockopt,
 866     .compat_getsockopt = compat_sock_common_getsockopt,
 867 #endif
 868 };
從各個函數(shù)指針的名稱,我們就可以大約知道它們是做什么事的了.進一步進以看到,
它們的函數(shù)指針指向的函數(shù)差不多都是相同的.除了一些細節(jié)上的區(qū)別,例如后面兩種協(xié)議類型并不支持listen.

socket()API第二個參數(shù)是協(xié)議類型,第三個參數(shù)是該協(xié)議類型下的協(xié)議——不過對于ipv4而言,
它們都是一一對應(yīng)的,但是從抽像封裝的角度看,數(shù)據(jù)結(jié)構(gòu)的設(shè)計本身應(yīng)該滿足一個協(xié)議類型下邊可能存在多個不同的協(xié)議,
即一對多的情況.而一一對應(yīng),僅是它們的特例:
876 /* Upon startup we insert all the elements in inetsw_array[] into
 877  * the linked list inetsw.
 878  */
 879 static struct inet_protosw inetsw_array[] =
 880 {
 881         {
 882                 .type =       SOCK_STREAM,
 883                 .protocol =   IPPROTO_TCP,
 884                 .prot =       &tcp_prot,
 885                 .ops =        &inet_stream_ops,
 886                 .capability = -1,
 887                 .no_check =   0,
 888                 .flags =      INET_PROTOSW_PERMANENT |
 889                   INET_PROTOSW_ICSK,
 890         },
 892         {
 893                 .type =       SOCK_DGRAM,
 894                 .protocol =   IPPROTO_UDP,
 895                 .prot =       &udp_prot,
 896                 .ops =        &inet_dgram_ops,
 897                 .capability = -1,
 898                 .no_check =   UDP_CSUM_DEFAULT,
 899                 .flags =      INET_PROTOSW_PERMANENT,
 900        },
 903        {
 904                .type =       SOCK_RAW,
 905                .protocol =   IPPROTO_IP,    /* wild card */
 906                .prot =       &raw_prot,
 907                .ops =        &inet_sockraw_ops,
 908                .capability = CAP_NET_RAW,
 909                .no_check =   UDP_CSUM_DEFAULT,
 910                .flags =      INET_PROTOSW_REUSE,
 911        }
 912 };
數(shù)組的每一個元素,就是支持的一種協(xié)議名稱,例如IPOROTO_TCP,但是由于IPV4本身協(xié)議類型跟協(xié)議是一一對應(yīng)的,
所以沒有更多的.type= SOCK_xxx 了.這樣數(shù)組實現(xiàn)了對PF_INET協(xié)議族下支持的協(xié)議類型,
以及協(xié)議類型下邊的協(xié)議進行了封裝,雖然事實上它們是一一對應(yīng)的關(guān)系,不過理論上完全可能存在一對多的可能.

數(shù)組內(nèi),封裝的一個具體的協(xié)議,由 struct proto 結(jié)構(gòu)來描述
以 TCP協(xié)議為例,TCP協(xié)議的 sokcet 操作函數(shù)都被封裝在這里了。 
struct proto tcp_prot = {
        .name                        = "TCP",
        .owner                        = THIS_MODULE,
        .close                        = tcp_close,
        .connect                = tcp_v4_connect,
        .disconnect                = tcp_disconnect,
        .accept                        = tcp_accept,
        .ioctl                        = tcp_ioctl,
        .init                        = tcp_v4_init_sock,
        .destroy                = tcp_v4_destroy_sock,
        .shutdown                = tcp_shutdown,
        .setsockopt                = tcp_setsockopt,
        .getsockopt                = tcp_getsockopt,
        .sendmsg                = tcp_sendmsg,
        .recvmsg                = tcp_recvmsg,
        .backlog_rcv                = tcp_v4_do_rcv,
        .hash                        = tcp_v4_hash,
        .unhash                        = tcp_unhash,
        .get_port                = tcp_v4_get_port,
        .enter_memory_pressure        = tcp_enter_memory_pressure,
        .sockets_allocated        = &tcp_sockets_allocated,
        .memory_allocated        = &tcp_memory_allocated,
        .memory_pressure        = &tcp_memory_pressure,
        .sysctl_mem                = sysctl_tcp_mem,
        .sysctl_wmem                = sysctl_tcp_wmem,
        .sysctl_rmem                = sysctl_tcp_rmem,
        .max_header                = MAX_TCP_HEADER,
        .obj_size                = sizeof(struct tcp_sock),
}
分配struct sock
看完了PF_INET的協(xié)議簇,協(xié)議類型和協(xié)議(也就是socket調(diào)用的三個參數(shù))的封裝關(guān)系,它們通過了兩個數(shù)據(jù)結(jié)構(gòu)
inet_protosw,struct proto來描述,被一個數(shù)組inetsw_array所封裝.接下來看它的初始化工作:
static struct list_head inetsw[SOCK_MAX];
static int __init inet_init(void)
{
        ……
        /* Register the socket-side information for inet_create. */
        for (r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
                INIT_LIST_HEAD(r);
        for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
                inet_register_protosw(q);
        ……
}
inetsw是一個數(shù)組,其每一個元素都是一個鏈表首部,前面一個循環(huán)初始化之.后一個循環(huán)就值得注意了,
也就是函數(shù)
916 void inet_register_protosw(struct inet_protosw *p)
 917 {
 918     struct list_head *lh;
 919     struct inet_protosw *answer;
 920     int protocol = p->protocol;
 921     struct list_head *last_perm;
 922
 923     spin_lock_bh(&inetsw_lock);
 925     if (p->type >= SOCK_MAX)
 926         goto out_illegal;
 928     /* If we are trying to override a permanent protocol, bail. */
 929     answer = NULL;
 930     last_perm = &inetsw[p->type];
 931     list_for_each(lh, &inetsw[p->type]) {
 932         answer = list_entry(lh, struct inet_protosw, list);
 934         /* Check only the non-wild match. */
 935         if (INET_PROTOSW_PERMANENT & answer->flags) {
 936             if (protocol == answer->protocol)
 937                 break;
 938             last_perm = lh;
 939         }
 941         answer = NULL;
 942     }
 943     if (answer)
 944         goto out_permanent;
 943     if (answer)
 944         goto out_permanent;
 945
 946     /* Add the new entry after the last permanent entry if any, so that
 947      * the new entry does not override a permanent entry when matched with
 948      * a wild-card protocol. But it is allowed to override any existing
 949      * non-permanent entry.  This means that when we remove this entry, the
 950      * system automatically returns to the old behavior.
 951      */
 952     list_add_rcu(&p->list, last_perm);
 953 out:
 954     spin_unlock_bh(&inetsw_lock);
 955
 956     synchronize_net();
 957
 958     return;
 .....................
這個函數(shù)完成的工作就是把inetsw_array數(shù)組中相同的協(xié)議類型下邊的協(xié)議,
加入到inetsw對應(yīng)的協(xié)議類型的鏈表中去,因為事實上一對一的關(guān)系,所以這個函數(shù)要簡單得多
因為不存在其它成員,所以每一次list_entry都為空值,所以不存在覆蓋和追加的情況,直接調(diào)用
list_add_rcu(&p->list, last_perm);
把協(xié)議類型節(jié)點(struct inet_protosw類型的數(shù)組的某個元素)添加到鏈表(鏈表首部本身是一個數(shù)組,
數(shù)組索引是協(xié)議對應(yīng)的協(xié)議類型的值)的第一個成員.

OK,繞了這么大一圈子,了解了協(xié)議的封裝及鏈表的注冊. 現(xiàn)在回到inet_create中來
 220 /*
 221  *  Create an inet socket.
 222  */
 223
 224 static int inet_create(struct socket *sock, int protocol)
 225 {
 226     struct sock *sk;
 227     struct list_head *p;
 228     struct inet_protosw *answer;
 229     struct inet_sock *inet;
 230     struct proto *answer_prot;
 231     unsigned char answer_flags;
 232     char answer_no_check;
 233     int try_loading_module = 0;
 234     int err;
 235
 236     sock->state = SS_UNCONNECTED;
socket的初始狀態(tài)設(shè)置為“未連接”,這意味著面向連接的協(xié)議類型,如 tcp,在使用之前必須建立連接, 修改狀態(tài)位.

 237
 238     /* Look for the requested type/protocol pair. */
 239     answer = NULL;
 240 lookup_protocol:
 241     err = -ESOCKTNOSUPPORT;
 242     rcu_read_lock();
 243     list_for_each_rcu(p, &inetsw[sock->type]) {
 244         answer = list_entry(p, struct inet_protosw, list);
 245
 246         /* Check the non-wild match. */
 247         if (protocol == answer->protocol) {
 248             if (protocol != IPPROTO_IP)
 249                 break;
 250         } else {
 251             /* Check for the two wild cases. */
 252             if (IPPROTO_IP == protocol) {
 253                 protocol = answer->protocol;
 254                 break;
 255             }
 256             if (IPPROTO_IP == answer->protocol)
 257                 break;
 258         }
 259         err = -EPROTONOSUPPORT;
 260         answer = NULL;
 261     }                                                                                                                  
這個循環(huán)根據(jù)socket(2)調(diào)用的protocol把之前在鏈表中注冊的協(xié)議節(jié)點找出來.
一個問題是,因為一一對應(yīng)關(guān)系的存在,用戶態(tài)調(diào)用socket(2)的時候,常常第三個參數(shù)直接就置 0 了.
也就是這里protocol為 0.那內(nèi)核又如何處理這一默認值呢?
也就是protocol != answer->protocol,而是被if (IPPROTO_IP == protocol) 所匹配了.
這樣將protocol置為鏈表中第一個協(xié)議,而當(dāng)循環(huán)結(jié)束時,answer自然也是指向這個鏈表中的第一個注冊節(jié)點.
假設(shè)SOCK_STREAM下同時注冊了TCP和123,那么這里默認就取TCP了.當(dāng)然如果把123在inetsw_array數(shù)組中的
位置調(diào)前,那么就 默認取123了.

將創(chuàng)建的socket的ops函數(shù)指針集指向具體協(xié)議類型的.例如創(chuàng)建的是SOCK_STREAM,
那么就指向了inet_stream_ops.
 289     sock->ops = answer->ops;
answer_prot指針指向當(dāng)前要創(chuàng)建的socket的協(xié)議類型下邊的協(xié)議,如上例它就是IPPROTO_TCP的tcp_prot結(jié)構(gòu)
 290     answer_prot = answer->prot;
 291     answer_no_check = answer->no_check;
 292     answer_flags = answer->flags;
 293     rcu_read_unlock();
 294
 295     BUG_TRAP(answer_prot->slab != NULL);

接下來一個重要的工作,就是為 socket 分配一個sock,并初始化它
 298     sk = sk_alloc(PF_INET, GFP_KERNEL, answer_prot, 1);
 299     if (sk == NULL)
 300         goto out;
 301
 302     err = 0;
 303     sk->sk_no_check = answer_no_check;
 304     if (INET_PROTOSW_REUSE & answer_flags)
 305         sk->sk_reuse = 1;
 306
 307     inet = inet_sk(sk);
 308     inet->is_icsk = INET_PROTOSW_ICSK & answer_flags;
 309
 310     if (SOCK_RAW == sock->type) {
 311         inet->num = protocol;
 312         if (IPPROTO_RAW == protocol)
 313             inet->hdrincl = 1;
 314     }
 315
 316     if (ipv4_config.no_pmtu_disc)
 317         inet->pmtudisc = IP_PMTUDISC_DONT;
 318     else
 319         inet->pmtudisc = IP_PMTUDISC_WANT;

 321     inet->id = 0;
 322
 323     sock_init_data(sock, sk);
 324
 325     sk->sk_destruct    = inet_sock_destruct;
 326     sk->sk_family      = PF_INET;
 327     sk->sk_protocol    = protocol;
 328     sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
 329
 330     inet->uc_ttl    = -1;
 331     inet->mc_loop   = 1;
 332     inet->mc_ttl    = 1;
 333     inet->mc_index  = 0;
 334     inet->mc_list   = NULL;
 335
 336     sk_refcnt_debug_inc(sk);
 337
 338     if (inet->num) {
 339         /* It assumes that any protocol which allows
 340          * the user to assign a number at socket
 341          * creation time automatically
 342          * shares.
 343          */
 344         inet->sport = htons(inet->num);
 345         /* Add to protocol hash chains. */
 346         sk->sk_prot->hash(sk);
 347     }
 348
 349     if (sk->sk_prot->init) {
 350         err = sk->sk_prot->init(sk);
 351         if (err)
 352             sk_common_release(sk);
 353     }
 354 out:
 355     return err;
 356 out_rcu_unlock:
 357     rcu_read_unlock();  
 358     goto out;
 359 }
雖然create的代碼就到這兒了,不過要說清楚sk的分配,還得費上大力氣.每一個 Socket 套接字,
都有一個對應(yīng)的struct socket結(jié)構(gòu)來描述(內(nèi)核中一般使用名稱為sock),但是同時又有一個
struct sock結(jié)構(gòu)(內(nèi)核中一般使用名稱為 sk).兩者之間是一一對應(yīng)的關(guān)系.在后面的sock_init_data函數(shù)中可以看到
sk->sk_socket=sock;
sock->sk=sk;
這樣的代碼.

socket結(jié)構(gòu)和sock結(jié)構(gòu)實際上是同一個事物的兩個方面.不妨說socket結(jié)構(gòu)是面向進程和系統(tǒng)調(diào)用界面的側(cè)面,
而sock結(jié)構(gòu)則是面向底層驅(qū)動程序的側(cè)面.設(shè)計者把socket套接字中與文件系統(tǒng)關(guān)系比較密切的那一部份放在
socket結(jié)構(gòu)中而把與通信關(guān)系比較密切的那一部份,則單獨成為一個數(shù)結(jié)結(jié)構(gòu),那就是sock結(jié)構(gòu).
由于這兩部份邏輯上本來就是一體的,所以要通過指針互相指向?qū)Ψ叫纬梢粚σ坏年P(guān)系.

再暫時回到inet_init中來,初始化工作中有如下代碼:
1262     rc = proto_register(&tcp_prot, 1);
1263     if (rc)
1264         goto out;
1265
1266     rc = proto_register(&udp_prot, 1);
1267     if (rc)
1268         goto out_unregister_tcp_proto;
1269
1270     rc = proto_register(&raw_prot, 1);
1271     if (rc)
1272         goto out_unregister_udp_proto;
這里為每個protocol都調(diào)用了proto_register函數(shù),其重要功能之一就是根據(jù)協(xié)議的obj_size成員的大小,
為協(xié)議創(chuàng)建高速緩存.
1701 static DEFINE_RWLOCK(proto_list_lock);
1702 static LIST_HEAD(proto_list);
1703
1704 int proto_register(struct proto *prot, int alloc_slab)
1705 {
1706     char *request_sock_slab_name = NULL;
1707     char *timewait_sock_slab_name;
1708     int rc = -ENOBUFS;
1709
1710     if (alloc_slab) {

可以看到函數(shù)最重要的功能就是根據(jù)prot的obj_size成員的大小為協(xié)議創(chuàng)建高速緩存
1711         prot->slab = kmem_cache_create(prot->name, prot->obj_size, 0,
1712                            SLAB_HWCACHE_ALIGN, NULL, NULL);
1713
1714         if (prot->slab == NULL) {
1715             printk(KERN_CRIT "%s: Can't create sock SLAB cache!\n",
1716                    prot->name);
1717             goto out;
1718         }
1719
順便看到它的另一個重要的功能是維護一個以proto_list為首的鏈表
1758     write_lock(&proto_list_lock);
1759     list_add(&prot->node, &proto_list);
1760     write_unlock(&proto_list_lock);
這里要注意的是prot->obj_size的大小,它它非僅僅是一個sk的大小,以 TCP為例:
.obj_size = sizeof(struct tcp_sock)。稍后再來分析這個東東

回到inet_create()函數(shù)中來,其調(diào)用sk_alloc()分配一個sk
sk = sk_alloc(PF_INET, GFP_KERNEL, answer_prot, 1);
840 struct sock *sk_alloc(int family, gfp_t priority,
 841               struct proto *prot, int zero_it)
 842 {
 843     struct sock *sk = NULL;
 844     kmem_cache_t *slab = prot->slab;
 845
 846     if (slab != NULL)
 847         sk = kmem_cache_alloc(slab, priority);
 848     else
 849         sk = kmalloc(prot->obj_size, priority);
 850
 851     if (sk) {
 852         if (zero_it) {
 853             memset(sk, 0, prot->obj_size);
 854             sk->sk_family = family;
 855             /*
 856              * See comment in struct sock definition to understand
 857              * why we need sk_prot_creator -acme
 858              */
 859             sk->sk_prot = sk->sk_prot_creator = prot;
 860             sock_lock_init(sk);
 861         }
 862        
 863         if (security_sk_alloc(sk, family, priority))
 864             goto out_free;
 865
 866         if (!try_module_get(prot->owner))
 867             goto out_free;
 868     }
 869     return sk;
 870
 871 out_free:
 872     if (slab != NULL)
 873         kmem_cache_free(slab, sk);
 874     else
 875         kfree(sk);
 876     return NULL;               
 877 }
在之前創(chuàng)建的高速緩存中申請分配一個slab緩存項并清零,然后設(shè)置協(xié)議族,并把sk中的sk_prot與對應(yīng)的協(xié)議關(guān)聯(lián)起來

初始化sk

分配完成sk后另一個重要的功能就是初始化它,sk的成員相當(dāng)復(fù)雜,其主要的初始化工作是在函數(shù)sock_init_data()
中完成的.
1477 void sock_init_data(struct socket *sock, struct sock *sk)
1478 {
1479     skb_queue_head_init(&sk->sk_receive_queue);
1480     skb_queue_head_init(&sk->sk_write_queue);
1481     skb_queue_head_init(&sk->sk_error_queue);
sock結(jié)構(gòu)中有三個重要的雙向隊列分別是sk_receive_queue,sk_write_queue和sk_error_queue
從它們的名字就可以看出來其作用了
隊列并非采用通用的list_head來維護而是使用skb_buffer隊列
// 109 struct sk_buff_head {                                                                                                  
// 110     /* These two members must be first. */
// 111     struct sk_buff  *next;
// 112     struct sk_buff  *prev;
// 113    
// 114     __u32       qlen;
// 115     spinlock_t  lock;
// 116 }; 
這樣隊列中指向的每一個skb_buffer就是一個數(shù)據(jù)包,分別是接收、發(fā)送和投遞錯誤
剩余的就是初始化其它成員變量了,后面再來專門分析這些成員的作用
1482 #ifdef CONFIG_NET_DMA
1483     skb_queue_head_init(&sk->sk_async_wait_queue);
1484 #endif
1485
1486     sk->sk_send_head    =   NULL;
1487
1488     init_timer(&sk->sk_timer);
1489    
1490     sk->sk_allocation   =   GFP_KERNEL;
1491     sk->sk_rcvbuf       =   sysctl_rmem_default;
1492     sk->sk_sndbuf       =   sysctl_wmem_default;
1493     sk->sk_state        =   TCP_CLOSE;
1494     sk->sk_socket       =   sock;
1495
1496     sock_set_flag(sk, SOCK_ZAPPED);
1497
1498     if(sock)
1499     {
1500         sk->sk_type =   sock->type;
1501         sk->sk_sleep    =   &sock->wait;
1502         sock->sk    =   sk;
1503     } else
1504         sk->sk_sleep    =   NULL;
1505
1506     rwlock_init(&sk->sk_dst_lock);
1507     rwlock_init(&sk->sk_callback_lock);
1508     lockdep_set_class(&sk->sk_callback_lock,
1509                af_callback_keys + sk->sk_family);
1510
1511     sk->sk_state_change =   sock_def_wakeup;
1512     sk->sk_data_ready   =   sock_def_readable;
1513     sk->sk_write_space  =   sock_def_write_space;  
1514     sk->sk_error_report =   sock_def_error_report;
1515     sk->sk_destruct     =   sock_def_destruct;
1516
1517     sk->sk_sndmsg_page  =   NULL;
1518     sk->sk_sndmsg_off   =   0;
1519
1520     sk->sk_peercred.pid     =   0;
1521     sk->sk_peercred.uid =   -1;
1522     sk->sk_peercred.gid =   -1;
1523     sk->sk_write_pending    =   0;
1524     sk->sk_rcvlowat     =   1;
1525     sk->sk_rcvtimeo     =   MAX_SCHEDULE_TIMEOUT;
1526     sk->sk_sndtimeo     =   MAX_SCHEDULE_TIMEOUT;
1527
1528     sk->sk_stamp.tv_sec     = -1L;
1529     sk->sk_stamp.tv_usec    = -1L;
1530
1531     atomic_set(&sk->sk_refcnt, 1);
1532 }

inet_create函數(shù)中除了初始化sk成員的值還有一部份代碼是初始化一個inet的東東
 307     inet = inet_sk(sk);
 308     inet->is_icsk = INET_PROTOSW_ICSK & answer_flags;

inet是一個struct inet_sock結(jié)構(gòu)類型來看它的定義
struct inet_sock {
        /* sk and pinet6 has to be the first two members of inet_sock */
        struct sock                sk;
               ……
}
我們說sock是面向用戶態(tài)調(diào)用而sk是面向內(nèi)核驅(qū)動調(diào)用的,那sk是如何與協(xié)議棧交互的呢?
對于每一個類型的協(xié)議,為了與 sk 聯(lián)系起來都定義了一個struct XXX_sock結(jié)構(gòu)XXX是協(xié)議名
struct tcp_sock {
        /* inet_sock has to be the first member of tcp_sock */
        struct inet_sock        inet;
        int        tcp_header_len;        /* Bytes of tcp header to send                */
        ……

struct udp_sock {
        /* inet_sock has to be the first member */
        struct inet_sock inet;
        int                 pending;        /* Any pending frames ? */
        unsigned int         corkflag;        /* Cork is required */
          __u16                 encap_type;        /* Is this an Encapsulation socket? */
        /*
         * Following member retains the infomation to create a UDP header
         * when the socket is uncorked.
         */
        __u16                 len;                /* total length of pending frames */
};
 
struct raw_sock {
        /* inet_sock has to be the first member */
        struct inet_sock   inet;
        struct icmp_filter filter;
};
 
很明顯它們的結(jié)構(gòu)定義是“af_inet一般屬性+自己的私有屬性”, 因為它們的第一個成員總是inet 
 
現(xiàn)在回頭來找一下起初在af_inet.c中封裝協(xié)議注冊的時候size成員, 對于 tcp 而言:
.obj_size  = sizeof(struct tcp_sock),
其它協(xié)議類似.
以obj_size來確定每個slab緩存項分配的大小,所以我們就可說每次申請分配的實際上是一個struct XXX_sock
結(jié)構(gòu)大小的結(jié)構(gòu).因為都是定義于上層結(jié)構(gòu)的第一個成員,可以使用強制類型轉(zhuǎn)換來使用這塊分配的內(nèi)存空間.
例如inet = inet_sk(sk);
 
static inline struct inet_sock *inet_sk(const struct sock *sk)
{
        return (struct inet_sock *)sk;
}
 
struct tcp_sock *tp = tcp_sk(sk);
 
static inline struct tcp_sock *tcp_sk(const struct sock *sk)
{
        return (struct tcp_sock *)sk;
}
 
OK,inet_create()運行完,一個socket套接字基本上就創(chuàng)建完畢了,剩下的就是與文件系統(tǒng)掛鉤,回到最初的sys_socket()
函數(shù)中來,它在調(diào)用完sock_create()后,緊接著調(diào)用sock_map_fd()函數(shù)
 422 int sock_map_fd(struct socket *sock)
 423 {
 424     struct file *newfile;
 425     int fd = sock_alloc_fd(&newfile);
 426
 427     if (likely(fd >= 0)) {
 428         int err = sock_attach_fd(sock, newfile);
 429
 430         if (unlikely(err < 0)) {           
 431             put_filp(newfile);
 432             put_unused_fd(fd);
 433             return err;  
 434         }                
 435         fd_install(fd, newfile);   
 436     }                    
 437     return fd;           
 438 }
這個函數(shù)的核心思想在一開始就已經(jīng)分析過了.從進程的角度來講一個socket套接字就是一個特殊的已打開的文件.
前面分配好一個socket 后,這里要做的就是將它與文件系統(tǒng)拉上親戚關(guān)系.
首先獲取一個空閑的文件描述符號和file結(jié)構(gòu),然后在文件系統(tǒng)中分配一個目錄項(d_alloc),使其指向已經(jīng)分配的inode節(jié)
點(d_add),然后把其目錄項掛在sockfs文件系統(tǒng)的根目錄之下,并且把目錄項的指針d_op設(shè)置成
指向sockfs_dentry_operati,這個數(shù)據(jù)結(jié)構(gòu)通過函數(shù)指針提供他與文件路徑有關(guān)的操作.
static struct dentry_operations sockfs_dentry_operations = {
        .d_delete =        sockfs_delete_dentry,
};
最后一步就是將file結(jié)構(gòu)中的f_op和sock結(jié)構(gòu)中的i_fop都指向socket_file_ops,它是一個函數(shù)指針集,
指向了socket面向文件系統(tǒng)的用戶態(tài)調(diào)用的一些接口函數(shù).
static struct file_operations socket_file_ops = {
        .owner =        THIS_MODULE,
        .llseek =        no_llseek,
        .aio_read =        sock_aio_read,
        .aio_write =        sock_aio_write,
        .poll =                sock_poll,
        .unlocked_ioctl = sock_ioctl,
        .mmap =                sock_mmap,
        .open =                sock_no_open,        /* special open code to disallow open via /proc */
        .release =        sock_close,
        .fasync =        sock_fasync,
        .readv =        sock_readv,
        .writev =        sock_writev,
        .sendpage =        sock_sendpage
};
OK,到這里整個socket套接字的創(chuàng)建工作就宣告完成了

寫到這里,可以為 socket 的創(chuàng)建下一個小結(jié)了:
  1. 所謂創(chuàng)建socket,對內(nèi)核而言最重要的工作就是分配sock與sk
  2. sock面向上層系統(tǒng)調(diào)用,主要是與文件系統(tǒng)交互.通過進程的current指針的files,結(jié)合創(chuàng)建socket
    時返回的文件描符述,可以找到內(nèi) 核中對應(yīng)的struct file,再根據(jù)file的f_dentry可以找到對應(yīng)的目
    錄項,而目錄項struct dentry中,有d_inode指針,指向與sock封裝在一起的inode.sock又與
    sk指針互指一一對應(yīng).在這串結(jié)構(gòu)中有兩個重要的函數(shù)集指針,一個是文件系統(tǒng)struct file中的
    f_op指針,它指向了對應(yīng)的用戶態(tài)調(diào)用的read,write等操調(diào)用,但不支持open,
    另一個是struct socket結(jié)構(gòu),即sock的ops指針,它在inet_create()中被置為
    sock->ops = answer->ops指向具體協(xié)議類型的ops
    例如inet_stream_ops,inet_dgram_ops或者是inet_sockraw_ops等等
    它用來支持上層的socket的其它API調(diào)用
  3. sk面向內(nèi)核協(xié)議棧,協(xié)議棧與它的接口數(shù)據(jù)結(jié)構(gòu)是struct protoname_sock,該結(jié)構(gòu)中包含了一般性
    的inet結(jié)構(gòu)和自己的私有成員,struct inet_sock的第一個成員就是一個 sk 指針,而分配的sk實
    際上空間大小是struct protoname_sock,所以這三者可以通過強制類型轉(zhuǎn)換來獲取需要的指針
  4. 由于水平有限,文件系統(tǒng)的一些細節(jié)被我跳過了,sk 中的大多數(shù)成員變量的作用,也被我跳出過了.
    呵呵,還好,終于還是把這塊流程給初步分析出來了.另外當(dāng)時寫的時候,沒有想到會寫這么長,
    大大超出了每貼的字限制。所以,每個小節(jié)內(nèi)容跟標(biāo)題可能會有點對不上號。

posted on 2013-02-16 21:51 大龍 閱讀(1200) 評論(1)  編輯 收藏 引用

評論

# re: Linux TCP/IP協(xié)議棧之Socket的實現(xiàn)分析(一 套接字的創(chuàng)建) --- 轉(zhuǎn) 2013-08-09 15:31 wtd321@163.com

sadsd  回復(fù)  更多評論   


只有注冊用戶登錄后才能發(fā)表評論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
      <noscript id="pjuwb"></noscript>
            <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
              <dd id="pjuwb"></dd>
              <abbr id="pjuwb"></abbr>
              亚洲免费综合| 亚洲欧美日韩中文在线制服| 久久国产夜色精品鲁鲁99| 亚洲性色视频| 国产亚洲精品一区二555| 久久精品一本久久99精品| 欧美一区二区三区婷婷月色 | 欧美1区视频| 麻豆精品视频在线观看视频| 亚洲美女啪啪| 亚洲一区国产精品| 国产一区二区三区黄视频| 欧美va亚洲va日韩∨a综合色| 欧美国产日本在线| 欧美一区日韩一区| 久久综合色88| 亚洲香蕉伊综合在人在线视看| 亚洲永久精品大片| 最近中文字幕日韩精品| 亚洲一卡久久| 亚洲品质自拍| 性色av一区二区三区| 亚洲人成欧美中文字幕| 亚洲嫩草精品久久| 亚洲精品中文字幕在线观看| 亚洲综合第一| 亚洲伦理在线观看| 久久精品欧洲| 欧美一激情一区二区三区| 欧美成年人视频网站| 欧美在线国产| 欧美日韩亚洲免费| 麻豆成人在线播放| 国产精品美女久久久久久久 | 亚洲国产成人精品久久| 国产精品久久久久久久久借妻 | 久久午夜色播影院免费高清| 亚洲一区二区三区三| 免费毛片一区二区三区久久久| 午夜精品久久久久久久久久久久久 | 欧美无乱码久久久免费午夜一区| 久久久国产午夜精品| 欧美日韩亚洲综合| 免费日韩av片| 激情综合网激情| 亚洲欧美在线高清| 午夜精品福利在线| 欧美日韩一区三区| 亚洲精品1区| 亚洲精品精选| 美女视频网站黄色亚洲| 久久伊人免费视频| 黑人操亚洲美女惩罚| 午夜视频一区在线观看| 午夜精品理论片| 国产精品成人国产乱一区| 亚洲精品在线免费观看视频| 亚洲人体一区| 欧美高清成人| 亚洲伦理网站| 亚洲永久免费精品| 国产精品久久久久aaaa九色| 一区二区三区免费看| 亚洲免费影视第一页| 国产精品色在线| 亚洲女爱视频在线| 久久精品亚洲精品| 国产综合色精品一区二区三区| 欧美亚洲在线视频| 久久视频在线免费观看| 亚洲二区在线视频| 免费成人黄色| 亚洲美女中文字幕| 性欧美18~19sex高清播放| 国产精品日韩欧美大师| 午夜欧美视频| 蜜臀久久99精品久久久画质超高清| 在线精品高清中文字幕| 美女黄网久久| 亚洲美女色禁图| 亚洲欧美日韩国产综合| 国产欧美一区二区精品秋霞影院| 久久精品国产999大香线蕉| 欧美激情久久久久久| 制服丝袜亚洲播放| 国产日韩欧美三级| 欧美国产精品va在线观看| 一区二区三区欧美在线观看| 久久国产免费| 亚洲乱码国产乱码精品精可以看| 欧美日韩免费在线视频| 欧美在线不卡| 亚洲另类在线视频| 久久综合狠狠综合久久综青草 | 欧美资源在线| 亚洲成人资源| 国产精品久久91| 久久免费国产| 亚洲伊人一本大道中文字幕| 欧美11—12娇小xxxx| 亚洲欧美日产图| 伊人久久综合| 国产精品毛片大码女人 | 亚洲天堂成人在线视频| 欧美中文字幕在线| 香蕉久久夜色精品国产使用方法| 亚洲欧美日韩一区| 在线激情影院一区| 国产精品手机在线| 欧美电影在线观看| 欧美一区二区三区免费大片| 日韩午夜高潮| 欧美华人在线视频| 久久精品国产亚洲精品| 一区二区三区四区国产| 亚洲国产99| 好看不卡的中文字幕| 欧美日韩在线视频一区二区| 卡一卡二国产精品| 久久精品99久久香蕉国产色戒| 一区二区三区欧美亚洲| 91久久亚洲| 欧美国产亚洲另类动漫| 乱人伦精品视频在线观看| 午夜影院日韩| 亚洲欧美国产毛片在线| 一区二区欧美在线观看| 亚洲精品中文字幕在线观看| 亚洲国产视频直播| 永久免费精品影视网站| 国内自拍视频一区二区三区| 国产偷国产偷精品高清尤物| 国产精品嫩草影院av蜜臀| 欧美午夜免费| 国产精品乱子久久久久| 国产精品看片你懂得| 欧美午夜精品久久久| 欧美午夜精品一区| 国产精品久久久久久久久久久久| 欧美视频一区二区三区| 国产精品高精视频免费| 欧美图区在线视频| 国产精品欧美日韩一区| 国产精品试看| 国产亚洲成精品久久| 国内外成人免费激情在线视频| 国产一区自拍视频| 樱桃国产成人精品视频| 亚洲成色最大综合在线| 亚洲日本一区二区| 亚洲精品日韩欧美| 亚洲视频一区在线观看| 欧美一区二区三区另类 | 亚洲国产影院| 亚洲人成网在线播放| 欧美伊久线香蕉线新在线| 午夜一区二区三区不卡视频| 亚洲无毛电影| 欧美一区二区在线看| 久久久久一区| 欧美激情一区二区三区高清视频| 亚洲国产日韩精品| 一区二区三区鲁丝不卡| 亚洲欧美视频一区二区三区| 久久久精品国产一区二区三区| 久久综合九色综合网站| 欧美日韩三区| 国内精品嫩模av私拍在线观看 | 欧美激情一区二区三区在线视频| 欧美日韩一区三区| 国产一区清纯| 夜夜嗨av一区二区三区中文字幕 | 最新成人在线| 欧美一级电影久久| 欧美国产精品一区| 亚洲一区二区不卡免费| 久久久久欧美精品| 国产精品久久波多野结衣| 伊人久久成人| 亚洲高清色综合| 亚洲黄色天堂| 亚洲国产综合在线| 亚洲一区二区三区精品动漫| 巨乳诱惑日韩免费av| 亚洲免费观看| 美女精品国产| 国产一区二区看久久| 一本大道久久a久久综合婷婷| 久久精品亚洲国产奇米99| 亚洲精选在线观看| 久久免费国产精品1| 国产精品自在欧美一区| 亚洲美女在线视频| 免费欧美视频| 欧美一区二区三区四区视频| 国产精品扒开腿爽爽爽视频 | 欧美国产日韩一区二区在线观看 | 亚洲第一精品夜夜躁人人躁| 欧美一区二区日韩一区二区| 亚洲精品日韩综合观看成人91|