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

大龍的博客

常用鏈接

統(tǒng)計(jì)

最新評(píng)論

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

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

系統(tǒng)總?cè)肟?/strong>
Linux內(nèi)核為所有的與socket有關(guān)操作的API,提供了一個(gè)統(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將用戶(hù)態(tài)參數(shù)拷貝至數(shù)組a,但是問(wèn)題在于每個(gè)被調(diào)用的 API 的參數(shù)不盡相同,
那么每次拷貝的字節(jié)在小如何判斷.來(lái)看其第三個(gè)參數(shù) nargs[call],其中 call 是操作碼,后面有個(gè)大大的
switch...case 就是判斷它。對(duì)應(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ù)操作碼的不同,計(jì)算對(duì)應(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ù)后,就進(jìn)入一個(gè)switch...case...判斷操作碼,跳轉(zhuǎn)至對(duì)應(yīng)的系統(tǒng)接口.

sys_socket函數(shù)
操作碼 SYS_SOCKET 是由 sys_socket()實(shí)現(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 }
在分析這段代碼之前, 首先來(lái)看創(chuàng)建一個(gè)Socket, 對(duì)內(nèi)核而言,究竟意味著什么?究竟需要內(nèi)核干什么事?
當(dāng)用戶(hù)空間要?jiǎng)?chuàng)建一個(gè) socke 接口時(shí),會(huì)調(diào)用 API 函數(shù)
int socket(int domain, int type, int protocol)
其三個(gè)參數(shù)分別表示協(xié)議族,協(xié)議類(lèi)型(面向連接或無(wú)連接)以及協(xié)議

對(duì)于用戶(hù)態(tài)而言, 一個(gè)Socket, 就是一個(gè)特殊的已經(jīng)打開(kāi)的文件,為了對(duì)socket抽像出文件的概念,
內(nèi)核中為socket定義了一個(gè)專(zhuān)門(mén)的文件系統(tǒng)類(lèi)型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 };
在模塊初始化的時(shí)候,安裝該文件系統(tǒng): 
void __init sock_init(void)
{
        ……
        register_filesystem(&sock_fs_type);
        sock_mnt = kern_mount(&sock_fs_type);        
}
稍后還要回來(lái)繼續(xù)分析安裝中的一點(diǎn)細(xì)節(jié)

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

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

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

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 }
申請(qǐng)文件系統(tǒng)mnt結(jié)構(gòu), 調(diào)用之前注冊(cè)的sock_fs_type的get_sb成員函數(shù)指針, 獲取相應(yīng)的超級(jí)塊sb.
并將mnt->mnt_sb指向sock_fs_type中的超級(jí)塊

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 }
注意其第三個(gè)參數(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;
}
這里就是先獲取/分配一個(gè)超級(jí)塊,然后初始化超級(jí)塊的各成員,包括s_op,它封裝了對(duì)應(yīng)的功能函數(shù)表.
s_op自然就指向了sockfs_ops,那前面提到的new_inode()函數(shù)分配inode時(shí)調(diào)用的
sock_mnt->mnt_sb->s_op->alloc_inode(sock_mnt->mnt_sb);
這個(gè)alloc_inode函數(shù)指針也就是sockfs_ops的sock_alloc_inode()函數(shù)——轉(zhuǎn)了一大圈,終于指到它了.

來(lái)看看sock_alloc_inode是如何分配一個(gè)inode節(jié)點(diǎn)的
 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ù)先分配了一個(gè)用于封裝socket和inode的 ei,然后在高速緩存中為之申請(qǐng)了一塊空間.
這樣inode和socket就同時(shí)都被分配了,接下來(lái)初始化socket的各個(gè)成員,這些成員在后面都會(huì)一一提到
 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),都已一一分析了.
最后一個(gè)關(guān)鍵問(wèn)題,就是如何把socket與一個(gè)已打開(kāi)的文件,建立映射關(guān)系.

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

有了這些基礎(chǔ),如果要把一個(gè)socket與一個(gè)已打開(kāi)的文件建立映射,首先要做的就是為socket分配一個(gè)struct file,
并申請(qǐng)分配一個(gè)相應(yīng)的文件描述符fd. 因?yàn)閟ocket并不支持open方法,所以不能期望用戶(hù)界面通過(guò)調(diào)用open() API
來(lái)分配一個(gè)struct file,而是通過(guò)調(diào)用get_empty_filp來(lái)獲取
struct file *file = get_empty_filp()
fd = get_unused_fd();獲取一個(gè)空間的文件描述符
然后讓current的files指針的fd數(shù)組的fd索引項(xiàng)指向該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);
}
做到這一步,有了一個(gè)文件描述符fd和一個(gè)打開(kāi)的文件file,它們與當(dāng)前進(jìn)程相連,但是好像與創(chuàng)建的socket并無(wú)任何瓜葛.
要做的映射還是沒(méi)有進(jìn)展,struct file或者文件描述述fd或current都沒(méi)有任何能夠與 inode或者是socket相關(guān)的東東
這需要一個(gè)中間的橋梁,目錄項(xiàng):struct dentry結(jié)構(gòu)

因?yàn)橐粋€(gè)文件都有與其對(duì)應(yīng)的目錄項(xiàng):
struct file {
        struct list_head        f_list;
        struct dentry          *f_dentry;
        ……
而一個(gè)目錄項(xiàng):
struct dentry {
        ……
        struct inode *d_inode;                /* Where the name belongs to - NULL is negative */
d_inode 成員指向了與之對(duì)應(yīng)的 inode節(jié)點(diǎn)

之前已經(jīng)創(chuàng)建了一個(gè)inode節(jié)點(diǎn)和與之對(duì)應(yīng)的 socket. 所以現(xiàn)在要做的就是:
“先為當(dāng)前文件分配一個(gè)對(duì)應(yīng)的目錄項(xiàng),再將已創(chuàng)建的 inode節(jié)點(diǎn)安裝至該目錄項(xiàng)”
這樣一個(gè)完成的映射關(guān)系:
進(jìn)程,文件描述符,打開(kāi)文件,目錄項(xiàng),inode節(jié)點(diǎn),socket就完整地串起來(lái)了
 
基本要分析的一些前導(dǎo)的東東都一一羅列了,雖然已盡量避免陷入文件系統(tǒng)的細(xì)節(jié)分析,但是還是不可避免地進(jìn)入其中,
因?yàn)樗鼈冴P(guān)系實(shí)現(xiàn)太緊密了,現(xiàn)在可以來(lái)看套接字的創(chuàng)建過(guò)程了
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é)議封裝
接下來(lái),函數(shù)調(diào)用之前已經(jīng)注冊(cè)的inet_family_ops的函數(shù)指針create,也就是inet_create()函數(shù).
前面可以說(shuō)一個(gè)通用的socket已經(jīng)創(chuàng)建好了,這里要完成與協(xié)議本身相關(guān)的一些創(chuàng)建socket的工作.
這一部份的工作比較復(fù)雜,還是先來(lái)看看af_inet.c中的模塊初始化時(shí)候,做了哪些與此相關(guān)的工作.

要引入的第一個(gè)數(shù)據(jù)結(jié)構(gòu)是struct inet_protosw,它封裝了一個(gè)協(xié)議類(lèi)型(如 SOCK_STREAM,SOCK_DGRAM等)
與IP協(xié)議中對(duì)應(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é)議類(lèi)型,對(duì)于 ipv4 而言就是SOCK_STREAM,SOCK_DGRAM或者是SOCK_RAW之一.
protocol是傳輸層的協(xié)議號(hào),prot用于描述一個(gè)具體的傳輸層協(xié)議,而ops指向?qū)?yīng)的當(dāng)前協(xié)議類(lèi)型的操作函數(shù)集
針對(duì)不同的協(xié)議類(lèi)型,定義了不同的 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 };
從各個(gè)函數(shù)指針的名稱(chēng),我們就可以大約知道它們是做什么事的了.進(jìn)一步進(jìn)以看到,
它們的函數(shù)指針指向的函數(shù)差不多都是相同的.除了一些細(xì)節(jié)上的區(qū)別,例如后面兩種協(xié)議類(lèi)型并不支持listen.

socket()API第二個(gè)參數(shù)是協(xié)議類(lèi)型,第三個(gè)參數(shù)是該協(xié)議類(lèi)型下的協(xié)議——不過(guò)對(duì)于ipv4而言,
它們都是一一對(duì)應(yīng)的,但是從抽像封裝的角度看,數(shù)據(jù)結(jié)構(gòu)的設(shè)計(jì)本身應(yīng)該滿(mǎn)足一個(gè)協(xié)議類(lèi)型下邊可能存在多個(gè)不同的協(xié)議,
即一對(duì)多的情況.而一一對(duì)應(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ù)組的每一個(gè)元素,就是支持的一種協(xié)議名稱(chēng),例如IPOROTO_TCP,但是由于IPV4本身協(xié)議類(lèi)型跟協(xié)議是一一對(duì)應(yīng)的,
所以沒(méi)有更多的.type= SOCK_xxx 了.這樣數(shù)組實(shí)現(xiàn)了對(duì)PF_INET協(xié)議族下支持的協(xié)議類(lèi)型,
以及協(xié)議類(lèi)型下邊的協(xié)議進(jìn)行了封裝,雖然事實(shí)上它們是一一對(duì)應(yīng)的關(guān)系,不過(guò)理論上完全可能存在一對(duì)多的可能.

數(shù)組內(nèi),封裝的一個(gè)具體的協(xié)議,由 struct proto 結(jié)構(gòu)來(lái)描述
以 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é)議類(lèi)型和協(xié)議(也就是socket調(diào)用的三個(gè)參數(shù))的封裝關(guān)系,它們通過(guò)了兩個(gè)數(shù)據(jù)結(jié)構(gòu)
inet_protosw,struct proto來(lái)描述,被一個(gè)數(shù)組inetsw_array所封裝.接下來(lái)看它的初始化工作:
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是一個(gè)數(shù)組,其每一個(gè)元素都是一個(gè)鏈表首部,前面一個(gè)循環(huán)初始化之.后一個(gè)循環(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;
 .....................
這個(gè)函數(shù)完成的工作就是把inetsw_array數(shù)組中相同的協(xié)議類(lèi)型下邊的協(xié)議,
加入到inetsw對(duì)應(yīng)的協(xié)議類(lèi)型的鏈表中去,因?yàn)槭聦?shí)上一對(duì)一的關(guān)系,所以這個(gè)函數(shù)要簡(jiǎn)單得多
因?yàn)椴淮嬖谄渌蓡T,所以每一次list_entry都為空值,所以不存在覆蓋和追加的情況,直接調(diào)用
list_add_rcu(&p->list, last_perm);
把協(xié)議類(lèi)型節(jié)點(diǎn)(struct inet_protosw類(lèi)型的數(shù)組的某個(gè)元素)添加到鏈表(鏈表首部本身是一個(gè)數(shù)組,
數(shù)組索引是協(xié)議對(duì)應(yīng)的協(xié)議類(lèi)型的值)的第一個(gè)成員.

OK,繞了這么大一圈子,了解了協(xié)議的封裝及鏈表的注冊(cè). 現(xiàn)在回到inet_create中來(lái)
 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é)議類(lèi)型,如 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     }                                                                                                                  
這個(gè)循環(huán)根據(jù)socket(2)調(diào)用的protocol把之前在鏈表中注冊(cè)的協(xié)議節(jié)點(diǎn)找出來(lái).
一個(gè)問(wèn)題是,因?yàn)橐灰粚?duì)應(yīng)關(guān)系的存在,用戶(hù)態(tài)調(diào)用socket(2)的時(shí)候,常常第三個(gè)參數(shù)直接就置 0 了.
也就是這里protocol為 0.那內(nèi)核又如何處理這一默認(rèn)值呢?
也就是protocol != answer->protocol,而是被if (IPPROTO_IP == protocol) 所匹配了.
這樣將protocol置為鏈表中第一個(gè)協(xié)議,而當(dāng)循環(huán)結(jié)束時(shí),answer自然也是指向這個(gè)鏈表中的第一個(gè)注冊(cè)節(jié)點(diǎn).
假設(shè)SOCK_STREAM下同時(shí)注冊(cè)了TCP和123,那么這里默認(rèn)就取TCP了.當(dāng)然如果把123在inetsw_array數(shù)組中的
位置調(diào)前,那么就 默認(rèn)取123了.

將創(chuàng)建的socket的ops函數(shù)指針集指向具體協(xié)議類(lèi)型的.例如創(chuàng)建的是SOCK_STREAM,
那么就指向了inet_stream_ops.
 289     sock->ops = answer->ops;
answer_prot指針指向當(dāng)前要?jiǎng)?chuàng)建的socket的協(xié)議類(lèi)型下邊的協(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);

接下來(lái)一個(gè)重要的工作,就是為 socket 分配一個(gè)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的代碼就到這兒了,不過(guò)要說(shuō)清楚sk的分配,還得費(fèi)上大力氣.每一個(gè) Socket 套接字,
都有一個(gè)對(duì)應(yīng)的struct socket結(jié)構(gòu)來(lái)描述(內(nèi)核中一般使用名稱(chēng)為sock),但是同時(shí)又有一個(gè)
struct sock結(jié)構(gòu)(內(nèi)核中一般使用名稱(chēng)為 sk).兩者之間是一一對(duì)應(yīng)的關(guān)系.在后面的sock_init_data函數(shù)中可以看到
sk->sk_socket=sock;
sock->sk=sk;
這樣的代碼.

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

再暫時(shí)回到inet_init中來(lái),初始化工作中有如下代碼:
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;
這里為每個(gè)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
順便看到它的另一個(gè)重要的功能是維護(hù)一個(gè)以proto_list為首的鏈表
1758     write_lock(&proto_list_lock);
1759     list_add(&prot->node, &proto_list);
1760     write_unlock(&proto_list_lock);
這里要注意的是prot->obj_size的大小,它它非僅僅是一個(gè)sk的大小,以 TCP為例:
.obj_size = sizeof(struct tcp_sock)。稍后再來(lái)分析這個(gè)東東

回到inet_create()函數(shù)中來(lái),其調(diào)用sk_alloc()分配一個(gè)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)建的高速緩存中申請(qǐng)分配一個(gè)slab緩存項(xiàng)并清零,然后設(shè)置協(xié)議族,并把sk中的sk_prot與對(duì)應(yīng)的協(xié)議關(guān)聯(lián)起來(lái)

初始化sk

分配完成sk后另一個(gè)重要的功能就是初始化它,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)中有三個(gè)重要的雙向隊(duì)列分別是sk_receive_queue,sk_write_queue和sk_error_queue
從它們的名字就可以看出來(lái)其作用了
隊(duì)列并非采用通用的list_head來(lái)維護(hù)而是使用skb_buffer隊(duì)列
// 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 }; 
這樣隊(duì)列中指向的每一個(gè)skb_buffer就是一個(gè)數(shù)據(jù)包,分別是接收、發(fā)送和投遞錯(cuò)誤
剩余的就是初始化其它成員變量了,后面再來(lái)專(zhuān)門(mén)分析這些成員的作用
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成員的值還有一部份代碼是初始化一個(gè)inet的東東
 307     inet = inet_sk(sk);
 308     inet->is_icsk = INET_PROTOSW_ICSK & answer_flags;

inet是一個(gè)struct inet_sock結(jié)構(gòu)類(lèi)型來(lái)看它的定義
struct inet_sock {
        /* sk and pinet6 has to be the first two members of inet_sock */
        struct sock                sk;
               ……
}
我們說(shuō)sock是面向用戶(hù)態(tài)調(diào)用而sk是面向內(nèi)核驅(qū)動(dòng)調(diào)用的,那sk是如何與協(xié)議棧交互的呢?
對(duì)于每一個(gè)類(lèi)型的協(xié)議,為了與 sk 聯(lián)系起來(lái)都定義了一個(gè)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一般屬性+自己的私有屬性”, 因?yàn)樗鼈兊牡谝粋€(gè)成員總是inet 
 
現(xiàn)在回頭來(lái)找一下起初在af_inet.c中封裝協(xié)議注冊(cè)的時(shí)候size成員, 對(duì)于 tcp 而言:
.obj_size  = sizeof(struct tcp_sock),
其它協(xié)議類(lèi)似.
以obj_size來(lái)確定每個(gè)slab緩存項(xiàng)分配的大小,所以我們就可說(shuō)每次申請(qǐng)分配的實(shí)際上是一個(gè)struct XXX_sock
結(jié)構(gòu)大小的結(jié)構(gòu).因?yàn)槎际嵌x于上層結(jié)構(gòu)的第一個(gè)成員,可以使用強(qiáng)制類(lèi)型轉(zhuǎn)換來(lái)使用這塊分配的內(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()運(yùn)行完,一個(gè)socket套接字基本上就創(chuàng)建完畢了,剩下的就是與文件系統(tǒng)掛鉤,回到最初的sys_socket()
函數(shù)中來(lái),它在調(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 }
這個(gè)函數(shù)的核心思想在一開(kāi)始就已經(jīng)分析過(guò)了.從進(jìn)程的角度來(lái)講一個(gè)socket套接字就是一個(gè)特殊的已打開(kāi)的文件.
前面分配好一個(gè)socket 后,這里要做的就是將它與文件系統(tǒng)拉上親戚關(guān)系.
首先獲取一個(gè)空閑的文件描述符號(hào)和file結(jié)構(gòu),然后在文件系統(tǒng)中分配一個(gè)目錄項(xiàng)(d_alloc),使其指向已經(jīng)分配的inode節(jié)
點(diǎn)(d_add),然后把其目錄項(xiàng)掛在sockfs文件系統(tǒng)的根目錄之下,并且把目錄項(xiàng)的指針d_op設(shè)置成
指向sockfs_dentry_operati,這個(gè)數(shù)據(jù)結(jié)構(gòu)通過(guò)函數(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,它是一個(gè)函數(shù)指針集,
指向了socket面向文件系統(tǒng)的用戶(hù)態(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,到這里整個(gè)socket套接字的創(chuàng)建工作就宣告完成了

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

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

評(píng)論

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

sadsd  回復(fù)  更多評(píng)論   


只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   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>
              亚洲视屏在线播放| 亚洲在线视频观看| 亚洲欧洲久久| 亚洲网友自拍| 亚洲片区在线| 男人的天堂亚洲| 亚洲国产精品一区在线观看不卡| 性高湖久久久久久久久| 宅男精品视频| 国产精品三级视频| 欧美一区二区三区四区在线观看 | 午夜一级久久| 日韩网站免费观看| 欧美午夜精品久久久| 中文一区在线| 亚洲一区观看| 国产一区亚洲| 欧美h视频在线| 欧美1区2区3区| 日韩一级精品| 亚洲先锋成人| 国产在线乱码一区二区三区| 久久青青草原一区二区| 久久婷婷亚洲| 99精品99久久久久久宅男| 亚洲人成网站精品片在线观看| 欧美激情一区二区三区成人| 亚洲视频导航| 欧美伊久线香蕉线新在线| 永久91嫩草亚洲精品人人| 亚洲第一精品夜夜躁人人躁| 欧美精品在线播放| 午夜激情一区| 久久婷婷人人澡人人喊人人爽| 亚洲黄色一区| 一本色道久久精品| 国产一区二区三区久久| 欧美激情国产日韩| 国产精品久久夜| 老司机精品久久| 欧美日本成人| 久久福利资源站| 欧美成人影音| 欧美亚洲免费在线| 麻豆成人av| 午夜宅男久久久| 免费亚洲婷婷| 欧美在线一区二区| 免费观看日韩av| 午夜天堂精品久久久久| 久久天堂精品| 亚洲综合导航| 乱中年女人伦av一区二区| 亚洲综合欧美| 免费不卡欧美自拍视频| 欧美一级视频一区二区| 欧美国产第一页| 久久久精彩视频| 欧美视频在线播放| 猛男gaygay欧美视频| 国产精品久久久久9999| 男女视频一区二区| 国产欧美日本| 日韩天堂在线观看| 欧美婷婷久久| 欧美福利视频网站| 欧美视频中文字幕在线| 欧美mv日韩mv亚洲| 国产一区二区中文字幕免费看| 亚洲乱码国产乱码精品精天堂| 国产在线欧美日韩| 亚洲欧美色一区| 亚洲一区二区伦理| 欧美激情小视频| 蜜臀a∨国产成人精品| 国产精品一二三四区| 日韩视频在线一区二区三区| 亚洲欧洲精品一区二区| 久久久久久久久久久成人| 欧美一区二区福利在线| 欧美视频在线观看免费网址| 亚洲人成网站999久久久综合| 在线观看日韩国产| 久久精品综合| 久久久久久久综合| 国产午夜亚洲精品理论片色戒| 一区二区国产在线观看| aa国产精品| 欧美精品尤物在线| 亚洲精品少妇| 一本到高清视频免费精品| 你懂的网址国产 欧美| 欧美不卡视频| 亚洲激情在线激情| 欧美+日本+国产+在线a∨观看| 另类图片国产| 亚洲成色www久久网站| 久久久噜噜噜久久| 六月丁香综合| 亚洲国产日韩欧美在线99| 另类激情亚洲| 亚洲高清一区二区三区| 亚洲精品午夜| 欧美三级网址| 亚洲欧洲99久久| 久久亚洲欧美国产精品乐播| 狠狠做深爱婷婷久久综合一区| 久久精品视频亚洲| 亚洲成色777777女色窝| 99精品国产99久久久久久福利| 欧美日韩精品免费观看| 亚洲视频香蕉人妖| 久久深夜福利| 亚洲日韩欧美一区二区在线| 欧美日韩久久| 亚洲自拍电影| 久久亚洲综合网| 亚洲精品久久嫩草网站秘色| 欧美日韩免费在线| 翔田千里一区二区| 欧美刺激性大交免费视频| 日韩一级大片| 国产日产精品一区二区三区四区的观看方式 | 宅男66日本亚洲欧美视频| 国产精品二区影院| 欧美一区二区三区在线免费观看| 麻豆精品传媒视频| 欧美日韩喷水| 欧美在线观看视频一区二区三区| 久久天堂精品| 亚洲美女在线观看| 国产性天天综合网| 欧美顶级大胆免费视频| 亚洲一区二区免费视频| 欧美3dxxxxhd| 亚洲欧美成人网| 伊人久久久大香线蕉综合直播| 欧美破处大片在线视频| 欧美一区二区视频97| 亚洲精品一线二线三线无人区| 久久久精品动漫| 亚洲天堂黄色| 亚洲精品乱码久久久久久日本蜜臀| 国产精品日韩专区| 欧美精品入口| 久久免费黄色| 亚洲欧美日韩一区在线观看| 亚洲精品在线观看免费| 麻豆精品精华液| 欧美一区在线视频| 一区二区三区四区蜜桃| 亚洲高清一区二区三区| 国产日产欧美精品| 国产精品久久久久一区二区三区共| 欧美成人精品一区二区| 久久精品视频在线播放| 亚洲欧美成人在线| 中文高清一区| 日韩天堂在线观看| 亚洲欧洲一区二区三区久久| 猛干欧美女孩| 老司机精品福利视频| 欧美一区二区三区视频免费| 亚洲一区二区少妇| 一区二区欧美在线观看| 亚洲三级免费电影| 在线免费观看视频一区| 伊人成人在线视频| 精品成人国产| 激情伊人五月天久久综合| 国产日韩欧美中文| 国产精品美女久久| 国产精品视频yy9099| 国产精品日韩一区二区| 国产嫩草影院久久久久| 国产精品亚洲成人| 国产日产欧美一区| 国产一区深夜福利| 在线观看中文字幕亚洲| 亚洲第一网站免费视频| 亚洲国产一区二区三区青草影视| 亚洲第一天堂av| 亚洲精品国产系列| 亚洲免费av片| 亚洲综合三区| 欧美在线视频一区二区三区| 欧美影院在线播放| 久久综合色婷婷| 欧美高清视频在线 | 欧美激情第三页| 欧美日本韩国一区| 国产精品va在线播放我和闺蜜| 国产精品国产三级国产aⅴ无密码| 欧美午夜精品理论片a级大开眼界 欧美午夜精品理论片a级按摩 | 久久久久久精| 欧美成人午夜免费视在线看片| 男女激情久久| 亚洲美女在线观看| 亚洲一区二区3| 久久久久久久成人|