zoukankan      html  css  js  c++  java
  • 5.2【Linux 内核网络协议栈源码剖析】socket 函数剖析 ☆☆☆

    深度剖析网络协议栈中的 socket 函数,可以说是把前面介绍的串联起来,将网络协议栈各层关联起来。

    应用层

    FTP

    SMTP

    HTTP

    ...

    传输层

    TCP

    UDP

    网络层

    IP ICMP

    ARP

    链路层

    以太网

    令牌环

    FDDI

    ...

     
     

                          

    1、应用层——socket 函数

     为了执行网络I/O,一个进程必须做的第一件事就是调用socket函数,指定期望的通信协议类型。该函数只是作为一个简单的接口函数供用户调用,调用该函数后将进入内核栈进行系统调用sock_socket 函数。

    [cpp] view plain copy
     
     print?
    1. #include <sys/socket.h>  
    2. int socket(int family, int type, int protocol);  
    3.                             /*返回:非负描述字——成功, -1——出错 
    4. 其中family参数指明协议族,type参数指明套接口类型,后面protocol通常设为0,以选择所给定family 和 type组合的系统缺省值*/  

    2、BSD Socket 层——sock_socket 函数

    从应用层进入该函数是通过一个共同的入口函数 sys_socket

    [cpp] view plain copy
     
     print?
    1. /* 
    2.  *  System call vectors. Since I (RIB) want to rewrite sockets as streams, 
    3.  *  we have this level of indirection. Not a lot of overhead, since more of 
    4.  *  the work is done via read/write/select directly. 
    5.  * 
    6.  *  I'm now expanding this up to a higher level to separate the assorted 
    7.  *  kernel/user space manipulations and global assumptions from the protocol 
    8.  *  layers proper - AC. 
    9.  */  
    10. //本函数是网络栈专用操作函数集的总入口函数,主要是将请求分配,调用具体的底层函数进行处理  
    11. asmlinkage int sys_socketcall(int call, unsigned long *args)  
    12. {  
    13.     int er;  
    14.     switch(call)   
    15.     {  
    16.         case SYS_SOCKET://socket函数  
    17.             er=verify_area(VERIFY_READ, args, 3 * sizeof(long));  
    18.             if(er)  
    19.                 return er;  
    20.             return(sock_socket(get_fs_long(args+0),  
    21.                 get_fs_long(args+1),//返回地址上的值  
    22.                 get_fs_long(args+2)));//调用sock_socket函数  
    23.                   ……  
    24. }  

    下面就是sock_socket 函数主体

    [cpp] view plain copy
     
     print?
    1. /* 
    2.  *  系统调用,创建套接字socket。涉及到sock结构的创建. 
    3.  */  
    4.   
    5. static int sock_socket(int family, int type, int protocol)  
    6. {  
    7.     int i, fd;  
    8.     struct socket *sock;  
    9.     struct proto_ops *ops;  
    10.   
    11.     /* 匹配应用程序调用socket()函数时指定的协议 */  
    12.     for (i = 0; i < NPROTO; ++i)   
    13.     {  
    14.         if (pops[i] == NULL) continue;  
    15.         if (pops[i]->family == family) //设置域  
    16.             break;  
    17.     }  
    18.     //没有匹配的协议,则出错退出  
    19.     if (i == NPROTO)   
    20.     {  
    21.         return -EINVAL;  
    22.     }  
    23.     //根据family输入参数决定域操作函数集用于ops字段的赋值  
    24.     //操作函数集是跟域相关的,不同的域对应不同的操作函数集  
    25.     ops = pops[i];  
    26.   
    27. /* 
    28.  *  Check that this is a type that we know how to manipulate and 
    29.  *  the protocol makes sense here. The family can still reject the 
    30.  *  protocol later. 
    31.  */  
    32.   //套接字类型检查  
    33.     if ((type != SOCK_STREAM && type != SOCK_DGRAM &&  
    34.         type != SOCK_SEQPACKET && type != SOCK_RAW &&  
    35.         type != SOCK_PACKET) || protocol < 0)  
    36.             return(-EINVAL);  
    37.   
    38. /* 
    39.  *  Allocate the socket and allow the family to set things up. if 
    40.  *  the protocol is 0, the family is instructed to select an appropriate 
    41.  *  default. 
    42.  */  
    43.     //分配socket套接字结构  
    44.     if (!(sock = sock_alloc()))   
    45.     {  
    46.         printk("NET: sock_socket: no more sockets ");  
    47.         return(-ENOSR); /* Was: EAGAIN, but we are out of 
    48.                    system resources! */  
    49.     }  
    50.     //指定对应类型,协议,以及操作函数集  
    51.     sock->type = type;  
    52.     sock->ops = ops;  
    53.     //分配下层sock结构,sock结构是比socket结构更底层的表示一个套接字的结构  
    54.     //前面博文有说明:http://blog.csdn.net/wenqian1991/article/details/21740945  
    55.     //socket是通用的套接字结构体,而sock与具体使用的协议相关  
    56.     if ((i = sock->ops->create(sock, protocol)) < 0) //这里调用下层函数 create  
    57.     {  
    58.         sock_release(sock);//出错回滚销毁处理  
    59.         return(i);  
    60.     }  
    61.     //分配一个文件描述符并在后面返回给应用层序作为以后的操作句柄  
    62.     if ((fd = get_fd(SOCK_INODE(sock))) < 0)   
    63.     {  
    64.         sock_release(sock);  
    65.         return(-EINVAL);  
    66.     }  
    67.   
    68.     return(fd);//这个就是我们应用系统使用的套接字描述符  
    69. }  

    该要介绍的注释里,已经说明白了,可以看到,该函数又将调用下一层函数 create。(网络栈就是这样,上层调用下层函数)

    sock_socket 函数内部还调用了一个函数 sock_alloc(),该函数主要是分配一个 socket 套接字结构(实际上找到一个空闲的inode结构,socket结构已经包含在inode结构中)

    [cpp] view plain copy
     
     print?
    1. /* 
    2.  *  分配一个socket结构 
    3.  */  
    4. struct socket *sock_alloc(void)  
    5. {  
    6.     struct inode * inode;  
    7.     struct socket * sock;  
    8.   
    9.     inode = get_empty_inode();//分配一个inode对象  
    10.     if (!inode)  
    11.         return NULL;  
    12.     //获得的inode结构的初始化  
    13.     inode->i_mode = S_IFSOCK;  
    14.     inode->i_sock = 1;  
    15.     inode->i_uid = current->uid;  
    16.     inode->i_gid = current->gid;  
    17.     //可以看出socket结构体的实体空间,就已经存在了inode结构中的union类型中,  
    18.     //所以无需单独的开辟空间分配一个socket 结构  
    19.     sock = &inode->u.socket_i;//这里把inode的union结构中的socket变量地址传给sock  
    20.     sock->state = SS_UNCONNECTED;  
    21.     sock->flags = 0;  
    22.     sock->ops = NULL;  
    23.     sock->data = NULL;  
    24.     sock->conn = NULL;  
    25.     sock->iconn = NULL;  
    26.     sock->next = NULL;  
    27.     sock->wait = &inode->i_wait;  
    28.     sock->inode = inode;//回绑  
    29.     sock->fasync_list = NULL;  
    30.     sockets_in_use++;//系统当前使用的套接字数量加1  
    31.     return sock;  
    32. }  

    3、INET Socket 层——inet_create 函数

    [cpp] view plain copy
     
     print?
    1. /* 
    2.  *  Create an inet socket. 
    3.  * 
    4.  *  FIXME: Gcc would generate much better code if we set the parameters 
    5.  *  up in in-memory structure order. Gcc68K even more so 
    6.  */  
    7.  //该函数被上层sock_socket函数调用,用于创建一个socket套接字对应的sock结构并对其进行初始化  
    8.  //socket是通用结构,sock是具体到某种协议的结构  
    9.  //代码是一大串,功能就是建立套接字对应的sock结构并对其进行初始化  
    10. static int inet_create(struct socket *sock, int protocol)  
    11. {  
    12.     struct sock *sk;  
    13.     struct proto *prot;  
    14.     int err;  
    15.     //分配一个sock结构,内存分配一个实体  
    16.     sk = (struct sock *) kmalloc(sizeof(*sk), GFP_KERNEL);  
    17.     if (sk == NULL)   
    18.         return(-ENOBUFS);  
    19.     sk->num = 0;//本地端口号  
    20.     sk->reuse = 0;  
    21.     //根据类型进行相关字段的赋值  
    22.     //关于哪种类型与协议的对应关系,请参考<UNP 卷1>,有些类型就只能和某种协议对应  
    23.     switch(sock->type)   
    24.     {  
    25.         case SOCK_STREAM:  
    26.         case SOCK_SEQPACKET:  
    27.             if (protocol && protocol != IPPROTO_TCP)   
    28.             {  
    29.                 kfree_s((void *)sk, sizeof(*sk));  
    30.                 return(-EPROTONOSUPPORT);  
    31.             }  
    32.             protocol = IPPROTO_TCP;//tcp协议  
    33.             sk->no_check = TCP_NO_CHECK;  
    34.             //这个prot变量表明了套接字使用的是何种协议  
    35.             //然后使用的则是对应协议的操作函数  
    36.             prot = &tcp_prot;  
    37.             break;  
    38.   
    39.         case SOCK_DGRAM:  
    40.             if (protocol && protocol != IPPROTO_UDP)   
    41.             {  
    42.                 kfree_s((void *)sk, sizeof(*sk));  
    43.                 return(-EPROTONOSUPPORT);  
    44.             }  
    45.             protocol = IPPROTO_UDP;//udp协议  
    46.             sk->no_check = UDP_NO_CHECK;//不使用校验  
    47.             prot=&udp_prot;  
    48.             break;  
    49.         
    50.         case SOCK_RAW:  
    51.             if (!suser()) //超级用户才能处理  
    52.             {  
    53.                 kfree_s((void *)sk, sizeof(*sk));  
    54.                 return(-EPERM);  
    55.             }  
    56.             if (!protocol)// 原始套接字类型,这里表示端口号  
    57.             {  
    58.                 kfree_s((void *)sk, sizeof(*sk));  
    59.                 return(-EPROTONOSUPPORT);  
    60.             }  
    61.             prot = &raw_prot;  
    62.             sk->reuse = 1;  
    63.             sk->no_check = 0;    /* 
    64.                          * Doesn't matter no checksum is 
    65.                          * performed anyway. 
    66.                          */  
    67.             sk->num = protocol;//本地端口号  
    68.             break;  
    69.   
    70.         case SOCK_PACKET:  
    71.             if (!suser())   
    72.             {  
    73.                 kfree_s((void *)sk, sizeof(*sk));  
    74.                 return(-EPERM);  
    75.             }  
    76.             if (!protocol)   
    77.             {  
    78.                 kfree_s((void *)sk, sizeof(*sk));  
    79.                 return(-EPROTONOSUPPORT);  
    80.             }  
    81.             prot = &packet_prot;  
    82.             sk->reuse = 1;  
    83.             sk->no_check = 0;    /* Doesn't matter no checksum is 
    84.                          * performed anyway. 
    85.                          */  
    86.             sk->num = protocol;  
    87.             break;  
    88.   
    89.         default://不符合以上任何类型,则返回  
    90.             kfree_s((void *)sk, sizeof(*sk));  
    91.             return(-ESOCKTNOSUPPORT);  
    92.     }  
    93.     sk->socket = sock;//建立与其对应的socket之间的关系  
    94. #ifdef CONFIG_TCP_NAGLE_OFF  
    95.     sk->nonagle = 1;//如果定义了Nagle算法  
    96. #else      
    97.     sk->nonagle = 0;  
    98. #endif    
    99.     //各种初始化  
    100.     //这里是sock结构  
    101.     sk->type = sock->type;  
    102.     sk->stamp.tv_sec=0;  
    103.     sk->protocol = protocol;  
    104.     sk->wmem_alloc = 0;  
    105.     sk->rmem_alloc = 0;  
    106.     sk->sndbuf = SK_WMEM_MAX;  
    107.     sk->rcvbuf = SK_RMEM_MAX;  
    108.     sk->pair = NULL;  
    109.     sk->opt = NULL;  
    110.     sk->write_seq = 0;  
    111.     sk->acked_seq = 0;  
    112.     sk->copied_seq = 0;  
    113.     sk->fin_seq = 0;  
    114.     sk->urg_seq = 0;  
    115.     sk->urg_data = 0;  
    116.     sk->proc = 0;  
    117.     sk->rtt = 0;             /*TCP_WRITE_TIME << 3;*/  
    118.     sk->rto = TCP_TIMEOUT_INIT;      /*TCP_WRITE_TIME*/  
    119.     sk->mdev = 0;  
    120.     sk->backoff = 0;  
    121.     sk->packets_out = 0;  
    122.     sk->cong_window = 1; /* start with only sending one packet at a time. */  
    123.     sk->cong_count = 0;  
    124.     sk->ssthresh = 0;  
    125.     sk->max_window = 0;  
    126.     sk->urginline = 0;  
    127.     sk->intr = 0;  
    128.     sk->linger = 0;  
    129.     sk->destroy = 0;  
    130.     sk->priority = 1;  
    131.     sk->shutdown = 0;  
    132.     sk->keepopen = 0;  
    133.     sk->zapped = 0;  
    134.     sk->done = 0;  
    135.     sk->ack_backlog = 0;  
    136.     sk->window = 0;  
    137.     sk->bytes_rcv = 0;  
    138.     sk->state = TCP_CLOSE;  
    139.     sk->dead = 0;  
    140.     sk->ack_timed = 0;  
    141.     sk->partial = NULL;  
    142.     sk->user_mss = 0;  
    143.     sk->debug = 0;  
    144.   
    145.     /* this is how many unacked bytes we will accept for this socket.  */  
    146.     sk->max_unacked = 2048; /* needs to be at most 2 full packets. */  
    147.   
    148.     /* how many packets we should send before forcing an ack.  
    149.        if this is set to zero it is the same as sk->delay_acks = 0 */  
    150.     sk->max_ack_backlog = 0;  
    151.     sk->inuse = 0;  
    152.     sk->delay_acks = 0;  
    153.     skb_queue_head_init(&sk->write_queue);  
    154.     skb_queue_head_init(&sk->receive_queue);  
    155.     sk->mtu = 576;//最大传输单元  
    156.     sk->prot = prot;  
    157.     sk->sleep = sock->wait;  
    158.     sk->daddr = 0;//远端地址  
    159.     sk->saddr = 0 /* 本地地址 */;  
    160.     sk->err = 0;  
    161.     sk->next = NULL;  
    162.     sk->pair = NULL;  
    163.     sk->send_tail = NULL;  
    164.     sk->send_head = NULL;  
    165.     sk->timeout = 0;  
    166.     sk->broadcast = 0;  
    167.     sk->localroute = 0;  
    168.     init_timer(&sk->timer);  
    169.     init_timer(&sk->retransmit_timer);  
    170.     sk->timer.data = (unsigned long)sk;  
    171.     sk->timer.function = &net_timer;  
    172.     skb_queue_head_init(&sk->back_log);  
    173.     sk->blog = 0;  
    174.     sock->data =(void *) sk;  
    175.   
    176.     //下面是sock结构中tcp首部初始化  
    177.     sk->dummy_th.doff = sizeof(sk->dummy_th)/4;  
    178.     sk->dummy_th.res1=0;  
    179.     sk->dummy_th.res2=0;  
    180.     sk->dummy_th.urg_ptr = 0;  
    181.     sk->dummy_th.fin = 0;  
    182.     sk->dummy_th.syn = 0;  
    183.     sk->dummy_th.rst = 0;  
    184.     sk->dummy_th.psh = 0;  
    185.     sk->dummy_th.ack = 0;  
    186.     sk->dummy_th.urg = 0;  
    187.     sk->dummy_th.dest = 0;  
    188.     //ip部分  
    189.     sk->ip_tos=0;  
    190.     sk->ip_ttl=64;  
    191. #ifdef CONFIG_IP_MULTICAST  
    192.     sk->ip_mc_loop=1;  
    193.     sk->ip_mc_ttl=1;  
    194.     *sk->ip_mc_name=0;  
    195.     sk->ip_mc_list=NULL;  
    196. #endif  
    197.       
    198.     sk->state_change = def_callback1;  
    199.     sk->data_ready = def_callback2;  
    200.     sk->write_space = def_callback3;  
    201.     sk->error_report = def_callback1;  
    202.   
    203.     if (sk->num) //如果分配了本地端口号  
    204.     {  
    205.     /* 
    206.      * It assumes that any protocol which allows 
    207.      * the user to assign a number at socket 
    208.      * creation time automatically 
    209.      * shares. 
    210.      */  
    211.      //将具有确定端口号的新sock结构加入到sock_array数组表示的sock结构链表中  
    212.         put_sock(sk->num, sk);//实际上这里确定的端口号一般为初始化0  
    213.         sk->dummy_th.source = ntohs(sk->num);//tcp首部源端地址,就是端口号  
    214.         //这里需要进行字节序转换,网络字节序转主机字节序  
    215.     }  
    216.   
    217.     if (sk->prot->init) //根据不同协议类型,调用对应init函数  
    218.     {  
    219.         err = sk->prot->init(sk);//调用相对应4层协议的初始化函数  
    220.         if (err != 0)   
    221.         {  
    222.             destroy_sock(sk);//出错了,就销毁  
    223.             return(err);  
    224.         }  
    225.     }  
    226.     return(0);  
    227. }  

    到这里一个 socket 套接字就创建完成了
    可以看出socket 套接字的创建过程为:socket() -> sock_socket() -> inet_create() 

    我们简单的总结一下这几个函数的功能:

    sock_socket() 内部的主要结构是 socket 结构体,其主要负责socket 结构体的创建(sock_alloc())和初始化,以及指定socket套接字的类型和操作函数集,然后分配一个文件描述符作为socket套接字的操作句柄,该描述符就是我们常说的套接字描述符。socket 的创建主要是分配一个inode 对象来说实现的。inode 对面内部有一个 union 类型变量,里面包含了各种类型的结构体,这里采用的 socket 类型,然后二者建立关联,inode中的union采用socket,socket结构中的inode指针指向该inode对象。

    inet_create() 内部的主要结构是 sock 结构体,sock 结构体比socket 结构更显复杂,其使用范围也更为广泛,socket 结构体是一个通用的结构,不涉及到具体的协议,而sock 结构则与具体的协议挂钩,属于具体层面上的一个结构。inet_create 函数的主要功能则是创建一个 sock 结构(kmalloc())然后根据上层传值下来的协议(通常是类型与地址族组合成成对应的协议)进行初始化。最后将创建好的 sock 结构插入到 sock 表中。

    网络栈的更下层用到的套接字就是 sock 结构体,在inet_create 函数中sock 套接字已经创建且初始化,socket() 至此完成。

    有了源码,更清楚的了解到socket 函数的功能:创建套接字(sock struct),指定期望的通信协议类型。

    到了这一步,套接字拥有自己的实体部分,指定了通信协议类型,但是既没有绑定本地地址信息(ip地址和端口号),也不知道对端的地址信息。

  • 相关阅读:
    扑克牌顺子
    数组转 二叉树 ,并且输出二叉树的右视图
    数组中逆序对(归并排序思想)
    链表数字加和
    回文数字
    数组中 只出现一次的数
    判断是否有从根节点到叶子节点的节点值之和等于 sum
    双指针求 3个数和 为0的 数
    vue项目将css,js全部打包到html文件配置
    webpack4配置优化
  • 原文地址:https://www.cnblogs.com/Ph-one/p/6423470.html
Copyright © 2011-2022 走看看