zoukankan      html  css  js  c++  java
  • 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函数进行套接字的初始化,主要是进行缓存的初始化

    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协议族的初始化函数

    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的定义中有四个元素:

    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下面是它的实现

    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:

    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函数进行套接字的创建:

    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的取值如下:

    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结构

    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,直接进行如下强制转换

    1. inet = inet_sk(sk);  

    1. static inline struct inet_sock *inet_sk(const struct sock *sk)  
    2. {  
    3.     return (struct inet_sock *)sk;  
    4. }  

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

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

    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函数分配:

    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强制转换来的:

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

  • 相关阅读:
    Mongodb
    Java原子类
    volatile
    uniapp输入空格
    看不见的的html
    小程序隐藏scroll-view滚动条的方法
    云函数调用云函数 openid不存在
    vue路由中 Navigating to current location ("/xxx") is not allowed
    Vue: 单页面应用如何保持登录状态
    letter-spacing
  • 原文地址:https://www.cnblogs.com/hehehaha/p/6332903.html
Copyright © 2011-2022 走看看