Linux内核--网络协议栈深入分析(四)--套接字内核初始化和创建过程

本文分析基于Linux Kernel 3.2.1

原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7984238

更多请查看专栏http://blog.csdn.net/column/details/linux-kernel-net.html

作者:闫明

1、系统初始化过程中会调用sock_init函数进行套接字的初始化,主要是进行缓存的初始化

[cpp]  view plain copy
  1. static int __init sock_init(void)  
  2. {  
  3.     int err;  
  4.   
  5.      //初始化.sock缓存  
  6.   
  7.     sk_init();  
  8.   
  9.      //初始化sk_buff缓存  
  10.     skb_init();  
  11.   
  12.      //初始化协议模块缓存  
  13.   
  14.     init_inodecache();  
  15.         //注册文件系统类型  
  16.     err = register_filesystem(&sock_fs_type);  
  17.     if (err)  
  18.         goto out_fs;  
  19.     sock_mnt = kern_mount(&sock_fs_type);  
  20.     if (IS_ERR(sock_mnt)) {  
  21.         err = PTR_ERR(sock_mnt);  
  22.         goto out_mount;  
  23.     }  
  24.   
  25. .........................  
  26. out:  
  27.     return err;  
  28.   
  29. out_mount:  
  30.     unregister_filesystem(&sock_fs_type);  
  31. out_fs:  
  32.     goto out;  
  33. }  


2、INET协议族的初始化函数

[cpp]  view plain copy
  1. static int __init inet_init(void)  
  2. {  
  3.     struct sk_buff *dummy_skb;  
  4.     struct inet_protosw *q;  
  5.     struct list_head *r;  
  6.     int rc = -EINVAL;  
  7.   
  8.     BUILD_BUG_ON(sizeof(struct inet_skb_parm) > sizeof(dummy_skb->cb));  
  9.   
  10.     sysctl_local_reserved_ports = kzalloc(65536 / 8, GFP_KERNEL);  
  11.     if (!sysctl_local_reserved_ports)  
  12.         goto out;  
  13.   
  14.     //下面注册传输层协议操作集  
  15.     rc = proto_register(&tcp_prot, 1);  
  16.     if (rc)  
  17.         goto out_free_reserved_ports;  
  18.   
  19.     rc = proto_register(&udp_prot, 1);  
  20.     if (rc)  
  21.         goto out_unregister_tcp_proto;  
  22.   
  23.     rc = proto_register(&raw_prot, 1);  
  24.     if (rc)  
  25.         goto out_unregister_udp_proto;  
  26.   
  27.     rc = proto_register(&ping_prot, 1);  
  28.     if (rc)  
  29.         goto out_unregister_raw_proto;  
  30.   
  31.     //注册INET协议族的handler  
  32.     (void)sock_register(&inet_family_ops);  
  33.   
  34. .........................  
  35.   
  36.     /* 
  37.      *  Add all the base protocols. 
  38.      */  
  39.     //将INET协议族协议数据包接收函数添加到系统中  
  40.     if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)  
  41.         printk(KERN_CRIT "inet_init: Cannot add ICMP protocol\n");  
  42.     if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)  
  43.         printk(KERN_CRIT "inet_init: Cannot add UDP protocol\n");  
  44.     if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)  
  45.         printk(KERN_CRIT "inet_init: Cannot add TCP protocol\n");  
  46. #ifdef CONFIG_IP_MULTICAST  
  47.     if (inet_add_protocol(&igmp_protocol, IPPROTO_IGMP) < 0)  
  48.         printk(KERN_CRIT "inet_init: Cannot add IGMP protocol\n");  
  49. #endif  
  50.   
  51.     /* Register the socket-side information for inet_create. */  
  52.     for (r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)  
  53.         INIT_LIST_HEAD(r);  
  54.     //将inetsw_array中的元素按套接字类型注册到inetsw链表数组中  
  55.     for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)  
  56.         inet_register_protosw(q);  
  57.   
  58.     /* 
  59.      *  Set the ARP module up 
  60.      */  
  61.   
  62.     arp_init();  
  63.   
  64.     /* 
  65.      *  Set the IP module up 
  66.      */  
  67.   
  68.     ip_init();  
  69.   
  70.     tcp_v4_init();  
  71.   
  72.     /* Setup TCP slab cache for open requests. */  
  73.     tcp_init();  
  74.   
  75.     /* Setup UDP memory threshold */  
  76.     udp_init();  
  77.   
  78.     /* Add UDP-Lite (RFC 3828) */  
  79.     udplite4_register();  
  80.   
  81.     ping_init();  
  82.   
  83.     /* 
  84.      *  Set the ICMP layer up 
  85.      */  
  86.   
  87.     if (icmp_init() < 0)  
  88.         panic("Failed to create the ICMP control socket.\n");  
  89.   
  90. .........................  
  91.     if (init_ipv4_mibs())  
  92.         printk(KERN_CRIT "inet_init: Cannot init ipv4 mibs\n");  
  93.   
  94.     ipv4_proc_init();  
  95.   
  96.     ipfrag_init();  
  97.   
  98.     dev_add_pack(&ip_packet_type);  
  99.   
  100.     rc = 0;  
  101. out:  
  102.     return rc;  
  103. out_unregister_raw_proto:  
  104.     proto_unregister(&raw_prot);  
  105. out_unregister_udp_proto:  
  106.     proto_unregister(&udp_prot);  
  107. out_unregister_tcp_proto:  
  108.     proto_unregister(&tcp_prot);  
  109. out_free_reserved_ports:  
  110.     kfree(sysctl_local_reserved_ports);  
  111.     goto out;  
  112. }  

上面函数中的inetsw_array的定义中有四个元素:

[cpp]  view plain copy
  1. static struct inet_protosw inetsw_array[] =  
  2. {  
  3.     {  
  4.         .type =       SOCK_STREAM,  
  5.         .protocol =   IPPROTO_TCP,  
  6.         .prot =       &tcp_prot,  
  7.         .ops =        &inet_stream_ops,  
  8.         .no_check =   0,  
  9.         .flags =      INET_PROTOSW_PERMANENT |  
  10.                   INET_PROTOSW_ICSK,  
  11.     },  
  12.   
  13.     {  
  14.         .type =       SOCK_DGRAM,  
  15.         .protocol =   IPPROTO_UDP,  
  16.         .prot =       &udp_prot,  
  17.         .ops =        &inet_dgram_ops,  
  18.         .no_check =   UDP_CSUM_DEFAULT,  
  19.         .flags =      INET_PROTOSW_PERMANENT,  
  20.        },  
  21.   
  22.        {  
  23.         .type =       SOCK_DGRAM,  
  24.         .protocol =   IPPROTO_ICMP,  
  25.         .prot =       &ping_prot,  
  26.         .ops =        &inet_dgram_ops,  
  27.         .no_check =   UDP_CSUM_DEFAULT,  
  28.         .flags =      INET_PROTOSW_REUSE,  
  29.        },  
  30.   
  31.        {  
  32.            .type =       SOCK_RAW,  
  33.            .protocol =   IPPROTO_IP,    /* wild card */  
  34.            .prot =       &raw_prot,  
  35.            .ops =        &inet_sockraw_ops,  
  36.            .no_check =   UDP_CSUM_DEFAULT,  
  37.            .flags =      INET_PROTOSW_REUSE,  
  38.        }  
  39. };  

上面的函数会将这个数组中的元素按照type为索引注册到inetsw指针数组中。

函数2中调用的sock_register函数就是想协议族数组net_families中添加inet协议族的net_proto_family的数据定义,主要是协议族的创建方法inet_create下面是它的实现

[cpp]  view plain copy
  1. int sock_register(const struct net_proto_family *ops)  
  2. {  
  3.     int err;  
  4.   
  5.     if (ops->family >= NPROTO) {  
  6.         printk(KERN_CRIT "protocol %d >= NPROTO(%d)\n", ops->family,  
  7.                NPROTO);  
  8.         return -ENOBUFS;  
  9.     }  
  10.   
  11.     spin_lock(&net_family_lock);  
  12.     if (rcu_dereference_protected(net_families[ops->family],  
  13.                       lockdep_is_held(&net_family_lock)))  
  14.         err = -EEXIST;  
  15.     else {  
  16.         RCU_INIT_POINTER(net_families[ops->family], ops);//这里就相当于将ops赋予net_families[ops->families]  
  17.         err = 0;  
  18.     }  
  19.     spin_unlock(&net_family_lock);  
  20.   
  21.     printk(KERN_INFO "NET: Registered protocol family %d\n", ops->family);  
  22.     return err;  
  23. }  

3、套接字的创建

套接字分BSD socket的传输层的socket(struct sock结构,与具体的传输层协议有关)。

3.1、BSD socket的创建

应用程序使用函数socket会产生系统调用,调用sys_socket函数来创建BSD socket:

[cpp]  view plain copy
  1. SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)  
  2. {  
  3.     int retval;  
  4.     struct socket *sock;  
  5.     int flags;  
  6.   
  7.     /* Check the SOCK_* constants for consistency.  */  
  8.     BUILD_BUG_ON(SOCK_CLOEXEC != O_CLOEXEC);  
  9.     BUILD_BUG_ON((SOCK_MAX | SOCK_TYPE_MASK) != SOCK_TYPE_MASK);  
  10.     BUILD_BUG_ON(SOCK_CLOEXEC & SOCK_TYPE_MASK);  
  11.     BUILD_BUG_ON(SOCK_NONBLOCK & SOCK_TYPE_MASK);  
  12.   
  13.     flags = type & ~SOCK_TYPE_MASK;  
  14.     if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))  
  15.         return -EINVAL;  
  16.     type &= SOCK_TYPE_MASK;  
  17.   
  18.     if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))  
  19.         flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;  
  20.   
  21.     retval = sock_create(family, type, protocol, &sock);//调用sock_create创建套接字,参数分别是协议族号、套接字类型,使用的传输层协议、执行要创建的套接字的指针的地址。  
  22.     if (retval < 0)  
  23.         goto out;  
  24.   
  25.     retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));  
  26.     if (retval < 0)  
  27.         goto out_release;  
  28.   
  29. out:  
  30.     /* It may be already another descriptor 8) Not kernel problem. */  
  31.     return retval;  
  32.   
  33. out_release:  
  34.     sock_release(sock);  
  35.     return retval;  
  36. }  
函数sock_create会调用__sock_create函数进行套接字的创建:

[cpp]  view plain copy
  1. int __sock_create(struct net *net, int family, int type, int protocol,  
  2.              struct socket **res, int kern)  
  3. {  
  4.     int err;  
  5.     struct socket *sock;  
  6.     const struct net_proto_family *pf;  
  7.   
  8.     /* 
  9.      *      合法性检查 
  10.      */  
  11.     if (family < 0 || family >= NPROTO)  
  12.         return -EAFNOSUPPORT;  
  13.     if (type < 0 || type >= SOCK_MAX)  
  14.         return -EINVAL;  
  15.   
  16.     /* Compatibility. 
  17.  
  18.        This uglymoron is moved from INET layer to here to avoid 
  19.        deadlock in module load. 
  20.      */  
  21.     if (family == PF_INET && type == SOCK_PACKET) {  
  22.         static int warned;  
  23.         if (!warned) {  
  24.             warned = 1;  
  25.             printk(KERN_INFO "%s uses obsolete (PF_INET,SOCK_PACKET)\n",  
  26.                    current->comm);  
  27.         }  
  28.         family = PF_PACKET;  
  29.     }  
  30.   
  31.     err = security_socket_create(family, type, protocol, kern);  
  32.     if (err)  
  33.         return err;  
  34.   
  35.     sock = sock_alloc();//分配inode结构并获得对应的socket结构  
  36.     if (!sock) {  
  37.         if (net_ratelimit())  
  38.             printk(KERN_WARNING "socket: no more sockets\n");  
  39.         return -ENFILE; /* Not exactly a match, but its the 
  40.                    closest posix thing */  
  41.     }  
  42.   
  43.     sock->type = type;  
  44.   
  45.     rcu_read_lock();  
  46.     pf = rcu_dereference(net_families[family]);  
  47.     err = -EAFNOSUPPORT;  
  48.     if (!pf)  
  49.         goto out_release;  
  50.   
  51.     /* 
  52.      * We will call the ->create function, that possibly is in a loadable 
  53.      * module, so we have to bump that loadable module refcnt first. 
  54.      */  
  55.     if (!try_module_get(pf->owner))//模块检测  
  56.         goto out_release;  
  57.   
  58.     /* Now protected by module ref count */  
  59.     rcu_read_unlock();  
  60.   
  61.     //这里调用inet_create函数对INET协议族进行创建  
  62.     err = pf->create(net, sock, protocol, kern);  
  63.     if (err < 0)  
  64.         goto out_module_put;  
  65.   
  66.     /* 
  67.      * Now to bump the refcnt of the [loadable] module that owns this 
  68.      * socket at sock_release time we decrement its refcnt. 
  69.      */  
  70.     if (!try_module_get(sock->ops->owner))  
  71.         goto out_module_busy;  
  72.   
  73.     /* 
  74.      * Now that we're done with the ->create function, the [loadable] 
  75.      * module can have its refcnt decremented 
  76.      */  
  77.     module_put(pf->owner);  
  78.     err = security_socket_post_create(sock, family, type, protocol, kern);  
  79.     if (err)  
  80.         goto out_sock_release;  
  81.     *res = sock;  
  82.   
  83.     return 0;  
  84.   
  85. out_module_busy:  
  86.     err = -EAFNOSUPPORT;  
  87. out_module_put:  
  88.     sock->ops = NULL;  
  89.     module_put(pf->owner);  
  90. out_sock_release:  
  91.     sock_release(sock);  
  92.     return err;  
  93.   
  94. out_release:  
  95.     rcu_read_unlock();  
  96.     goto out_sock_release;  
  97. }  
其中的参数protocol的取值如下:

[cpp]  view plain copy
  1. /* Standard well-defined IP protocols.  */  
  2. enum {  
  3.   IPPROTO_IP = 0,       /* Dummy protocol for TCP       */  
  4.   IPPROTO_ICMP = 1,     /* Internet Control Message Protocol    */  
  5.   IPPROTO_IGMP = 2,     /* Internet Group Management Protocol   */  
  6.   IPPROTO_IPIP = 4,     /* IPIP tunnels (older KA9Q tunnels use 94) */  
  7.   IPPROTO_TCP = 6,      /* Transmission Control Protocol    */  
  8.   IPPROTO_EGP = 8,      /* Exterior Gateway Protocol        */  
  9.   IPPROTO_PUP = 12,     /* PUP protocol             */  
  10.   IPPROTO_UDP = 17,     /* User Datagram Protocol       */  
  11.   IPPROTO_IDP = 22,     /* XNS IDP protocol         */  
  12.   IPPROTO_DCCP = 33,        /* Datagram Congestion Control Protocol */  
  13.   IPPROTO_RSVP = 46,        /* RSVP protocol            */  
  14.   IPPROTO_GRE = 47,     /* Cisco GRE tunnels (rfc 1701,1702)    */  
  15.   
  16.   IPPROTO_IPV6   = 41,      /* IPv6-in-IPv4 tunnelling      */  
  17.   
  18.   IPPROTO_ESP = 50,            /* Encapsulation Security Payload protocol */  
  19.   IPPROTO_AH = 51,             /* Authentication Header protocol       */  
  20.   IPPROTO_BEETPH = 94,         /* IP option pseudo header for BEET */  
  21.   IPPROTO_PIM    = 103,     /* Protocol Independent Multicast   */  
  22.   
  23.   IPPROTO_COMP   = 108,                /* Compression Header protocol */  
  24.   IPPROTO_SCTP   = 132,     /* Stream Control Transport Protocol    */  
  25.   IPPROTO_UDPLITE = 136,    /* UDP-Lite (RFC 3828)          */  
  26.   
  27.   IPPROTO_RAW    = 255,     /* Raw IP packets           */  
  28.   IPPROTO_MAX  
  29. };  


3.2、INET层socket(inet_socket)和传输层socket(struct sock)创建

函数inet_create完成了上述功能,并初始化了sock的属性值,将socket的sk属性指向sock结构

[cpp]  view plain copy
  1. static int inet_create(struct net *net, struct socket *sock, int protocol,  
  2.                int kern)  
  3. {  
  4.     struct sock *sk;  
  5.     struct inet_protosw *answer;  
  6.     struct inet_sock *inet;  
  7.     struct proto *answer_prot;  
  8.     unsigned char answer_flags;  
  9.     char answer_no_check;  
  10.     int try_loading_module = 0;  
  11.     int err;  
  12.   
  13.     if (unlikely(!inet_ehash_secret))  
  14.         if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM)  
  15.             build_ehash_secret();  
  16.   
  17.     sock->state = SS_UNCONNECTED;  
  18.   
  19.     /* Look for the requested type/protocol pair. */  
  20. lookup_protocol:  
  21.     err = -ESOCKTNOSUPPORT;  
  22.     rcu_read_lock();  
  23.     //根据传输层协议的类型创建sock结构  
  24.     //遍历inetsw链表  
  25.     list_for_each_entry_rcu(answer, &inetsw[sock->type], list) {  
  26.   
  27.         err = 0;  
  28.         /* Check the non-wild match. */  
  29.         if (protocol == answer->protocol) {  
  30.             if (protocol != IPPROTO_IP)  
  31.                 break;//找到了适配的inetsw[]元素  
  32.         } else {  
  33.             /* Check for the two wild cases. */  
  34.             if (IPPROTO_IP == protocol) {  
  35.                 protocol = answer->protocol;  
  36.                 break;  
  37.             }  
  38.             if (IPPROTO_IP == answer->protocol)  
  39.                 break;  
  40.         }  
  41.         err = -EPROTONOSUPPORT;  
  42.     }  
  43.     //到这里answer指向了合适的inetsw结构,若是TCP协议,answer指向内容如下  
  44.     /* 
  45.     *   .type =       SOCK_STREAM, 
  46.     *   .protocol =   IPPROTO_TCP, 
  47.     *   .prot =       &tcp_prot, 
  48.     *   .ops =        &inet_stream_ops, 
  49.     *   .no_check =   0, 
  50.     *   .flags =      INET_PROTOSW_PERMANENT | 
  51.     *             INET_PROTOSW_ICSK, 
  52.     */  
  53.     if (unlikely(err)) {  
  54.         if (try_loading_module < 2) {  
  55.             rcu_read_unlock();  
  56.             /* 
  57.              * Be more specific, e.g. net-pf-2-proto-132-type-1 
  58.              * (net-pf-PF_INET-proto-IPPROTO_SCTP-type-SOCK_STREAM) 
  59.              */  
  60.             if (++try_loading_module == 1)  
  61.                 request_module("net-pf-%d-proto-%d-type-%d",  
  62.                            PF_INET, protocol, sock->type);  
  63.             /* 
  64.              * Fall back to generic, e.g. net-pf-2-proto-132 
  65.              * (net-pf-PF_INET-proto-IPPROTO_SCTP) 
  66.              */  
  67.             else  
  68.                 request_module("net-pf-%d-proto-%d",  
  69.                            PF_INET, protocol);  
  70.             goto lookup_protocol;  
  71.         } else  
  72.             goto out_rcu_unlock;  
  73.     }  
  74.   
  75.     err = -EPERM;  
  76.     if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW))  
  77.         goto out_rcu_unlock;  
  78.   
  79.     err = -EAFNOSUPPORT;  
  80.     if (!inet_netns_ok(net, protocol))  
  81.         goto out_rcu_unlock;  
  82.   
  83.     sock->ops = answer->ops;  
  84.     answer_prot = answer->prot;  
  85.     answer_no_check = answer->no_check;  
  86.     answer_flags = answer->flags;  
  87.     rcu_read_unlock();  
  88.   
  89.     WARN_ON(answer_prot->slab == NULL);  
  90.   
  91.     err = -ENOBUFS;  
  92.     //分配sock结构体内存,这里在inet_init函数初始化好的高速缓冲区中分配内存,然后做一些初始化工作。后面有进一步分析。  
  93.     sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot);  
  94.     if (sk == NULL)  
  95.         goto out;  
  96.   
  97.     err = 0;  
  98.     sk->sk_no_check = answer_no_check;  
  99.     if (INET_PROTOSW_REUSE & answer_flags)  
  100.         sk->sk_reuse = 1;  
  101.   
  102.     inet = inet_sk(sk);//后面有进一步分析,为何可以强制转换?!!  
  103.     inet->is_icsk = (INET_PROTOSW_ICSK & answer_flags) != 0;  
  104.   
  105.     inet->nodefrag = 0;  
  106.   
  107.     if (SOCK_RAW == sock->type) {  
  108.         inet->inet_num = protocol;  
  109.         if (IPPROTO_RAW == protocol)  
  110.             inet->hdrincl = 1;  
  111.     }  
  112.   
  113.     if (ipv4_config.no_pmtu_disc)  
  114.         inet->pmtudisc = IP_PMTUDISC_DONT;  
  115.     else  
  116.         inet->pmtudisc = IP_PMTUDISC_WANT;  
  117.   
  118.     inet->inet_id = 0;  
  119.     //对sk进行初始化设置并将sock中的sk指针指向sk结构  
  120.     sock_init_data(sock, sk);  
  121.   
  122.     //进一步设置sk的其他属性信息  
  123.     sk->sk_destruct     = inet_sock_destruct;  
  124.     sk->sk_protocol     = protocol;  
  125.     sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;  
  126.   
  127.     inet->uc_ttl = -1;  
  128.     inet->mc_loop    = 1;  
  129.     inet->mc_ttl = 1;  
  130.     inet->mc_all = 1;  
  131.     inet->mc_index   = 0;  
  132.     inet->mc_list    = NULL;  
  133.   
  134.     sk_refcnt_debug_inc(sk);  
  135.   
  136.     if (inet->inet_num) {  
  137.         /* It assumes that any protocol which allows 
  138.          * the user to assign a number at socket 
  139.          * creation time automatically 
  140.          * shares. 
  141.          */  
  142.         inet->inet_sport = htons(inet->inet_num);  
  143.         /* Add to protocol hash chains. */  
  144.         sk->sk_prot->hash(sk);//调用inet_hash函数  
  145.     }  
  146.       
  147.     if (sk->sk_prot->init) {  
  148.         err = sk->sk_prot->init(sk);//调用tcp_v4_init_sock函数进行进一步的初始化,由于在函数sk_alloc中一些属性被设置成0了,所以在此调用进行初始化  
  149.         if (err)  
  150.             sk_common_release(sk);  
  151.     }  
  152. out:  
  153.     return err;  
  154. out_rcu_unlock:  
  155.     rcu_read_unlock();  
  156.     goto out;  
  157. }  

关于套接字struct sock与struct inet_sock、struct tcp_sock、struct inet_connection_sock等结构之间的关系有待进一步了解。

上篇中已经写过,内核中套接字struct socket、struct sock、struct inet_sock、struct tcp_sock、struct raw_sock、struct udp_sock、struct inet_connection_sock、struct inet_timewait_sock和struct tcp_timewait_sock的关系是:

*struct socket这个是BSD层的socket,应用程序会用过系统调用首先创建该类型套接字,它和具体协议无关。

*struct inet_sock是INET协议族使用的socket结构,可以看成位于INET层,是struct sock的一个扩展。它的第一个属性就是struct sock结构。

*struct sock是与具体传输层协议相关的套接字,所有内核的操作都基于这个套接字。

*struct tcp_sock是TCP协议的套接字表示,它是对struct inet_connection_sock的扩展,其第一个属性就是struct inet_connection_sock inet_conn。

*struct raw_sock是原始类型的套接字表示,ICMP协议就使用这种套接字,其是对struct sock的扩展。

*struct udp_sock是UDP协议套接字表示,其是对struct inet_sock套接字的扩展。

*struct inet_connetction_sock是所有面向连接协议的套接字,是对struct inet_sock套接字扩展。

后面两个是用于控制超时的套接字。

就拿struct inet_sock和struct sock为例来说明,为什么内核中可以直接将sock结构体首地址强制转换成inet_sock的首地址?并且inet_sock的大小要大于sock,直接进行如下强制转换

[cpp]  view plain copy
  1. inet = inet_sk(sk);  

[cpp]  view plain copy
  1. static inline struct inet_sock *inet_sk(const struct sock *sk)  
  2. {  
  3.     return (struct inet_sock *)sk;  
  4. }  

不会发生内存非法访问吗?!那就是在分配的时候并不只是分配的struct sock结构体大小的存储空间!

可以细看sock结构体分配的代码:

[cpp]  view plain copy
  1. struct sock *sk_alloc(struct net *net, int family, gfp_t priority,  
  2.               struct proto *prot)  
  3. {  
  4.     struct sock *sk;  
  5.   
  6.     sk = sk_prot_alloc(prot, priority | __GFP_ZERO, family);  
  7.     if (sk) {  
  8.         sk->sk_family = family;  
  9.         sk->sk_prot = sk->sk_prot_creator = prot;  
  10.         sock_lock_init(sk);  
  11.         sock_net_set(sk, get_net(net));  
  12.         atomic_set(&sk->sk_wmem_alloc, 1);  
  13.   
  14.         sock_update_classid(sk);  
  15.     }  
  16.   
  17.     return sk;  
  18. }  
紧接着调用sk_prot_alloc函数分配:

[cpp]  view plain copy
  1. static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority,  
  2.         int family)  
  3. {  
  4.     struct sock *sk;  
  5.     struct kmem_cache *slab;  
  6.   
  7.     slab = prot->slab;  
  8.     if (slab != NULL) {  
  9.         sk = kmem_cache_alloc(slab, priority & ~__GFP_ZERO);  
  10.         ..............................  
  11.     } else  
  12.         sk = kmalloc(prot->obj_size, priority);  
  13.   
  14.     .....................  
  15.   
  16.     return sk;  
  17. ......................  
  18. }  
上面的代码中首先判断高速缓存中是否可用,如果不可用,直接在内存分配空间,不过大小都是prot->obj_size。

如果是TCP协议中的tcp_prot中指明该属性的大小为.obj_size = sizeof(struct tcp_sock)。

所以,程序中给struct sock指针分配的不是该结构体的实际大小,而是大于其实际大小,以便其扩展套接字的属性占用。
以图例说明tcp_sock是如何从sock强制转换来的:

Linux内核--网络协议栈深入分析(四)--套接字内核初始化和创建过程_第1张图片

下篇将分析套接字的绑定、连接等一系列操作的实现。

你可能感兴趣的:(Linux内核--网络协议栈深入分析(四)--套接字内核初始化和创建过程)