Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1509550
  • 博文数量: 228
  • 博客积分: 1698
  • 博客等级: 上尉
  • 技术积分: 3241
  • 用 户 组: 普通用户
  • 注册时间: 2008-12-24 21:49
个人简介

Linux

文章分类

全部博文(228)

文章存档

2017年(1)

2016年(43)

2015年(102)

2014年(44)

2013年(5)

2012年(30)

2011年(3)

分类: LINUX

2014-08-14 14:27:36

上篇介绍到了pf->create函数,这个函数调用完成后socket的创建就算结束了。不同协议簇下pf->create的实现是不同的,这里暂时选择AF_INET协议簇进行分析,其他再补充。

先看一下pf->create对应了那个函数,以及使如何对应上的

点击(此处)折叠或打开

  1. static int __init inet_init(void)
  2. {
  3.     ......
  4.     (void)sock_register(&inet_family_ops);

  5.     for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
  6.         inet_register_protosw(q);
  7.     ......
  8. }
inet_init是在网络模块初始化要调用的初始化函数,这个函数完成的任务不少,暂时我们先关注上面两个部分,其余部分后续还会再回头来看。

点击(此处)折叠或打开

  1. static struct net_proto_family inet_family_ops = {
  2.     .family = PF_INET,
  3.     .create = inet_create,
  4.     .owner    = THIS_MODULE,
  5. };
  6. int sock_register(const struct net_proto_family *ops)
  7. {
  8.     ......
  9.     spin_lock(&net_family_lock);
  10.     if (net_families[ops->family])
  11.         err = -EEXIST;
  12.     else {
  13.         net_families[ops->family] = ops;
  14.         err = 0;
  15.     }
  16.     ......
  17. }
sock_register中会把inet_family_ops注册到net_families数组中,根据具体协议簇的类型进行索引调用,我们回想一下在__sock_create时,按照如下方式调用

点击(此处)折叠或打开

  1. pf = rcu_dereference(net_families[family]);
  2. err = pf->create(net, sock, protocol);
先根据用户指定的family在net_families中索引对应的net_proto_family的结构,AF_INET对应的是inet_family_ops,所以pf->create对应了inet_create函数。
下面看一下inet_create函数的实现

点击(此处)折叠或打开

  1. static int inet_create(struct net *net, struct socket *sock, int protocol)
  2. {
  3.     ......
  4.     sock->state = SS_UNCONNECTED;
  5.     ......
  6.     list_for_each_entry_rcu(answer, &inetsw[sock->type], list) {
  7.     ......
  8.     }
  9.     ......
  10.     sock->ops = answer->ops;
  11.     answer_prot = answer->prot;
  12.     answer_no_check = answer->no_check;
  13.     answer_flags = answer->flags;
  14.     ......
  15.     sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot);
  16.     if (sk == NULL)
  17.         goto out;

  18.     err = 0;
  19.     sk->sk_no_check = answer_no_check;
  20.     if (INET_PROTOSW_REUSE & answer_flags)
  21.         sk->sk_reuse = 1;

  22.     inet = inet_sk(sk);
  23.     inet->is_icsk = (INET_PROTOSW_ICSK & answer_flags) != 0;

  24.     if (SOCK_RAW == sock->type) {
  25.         inet->num = protocol;
  26.         if (IPPROTO_RAW == protocol)
  27.             inet->hdrincl = 1;
  28.     }

  29.     if (ipv4_config.no_pmtu_disc)
  30.         inet->pmtudisc = IP_PMTUDISC_DONT;
  31.     else
  32.         inet->pmtudisc = IP_PMTUDISC_WANT;

  33.     inet->id = 0;

  34.     sock_init_data(sock, sk);

  35.     sk->sk_destruct     = inet_sock_destruct;
  36.     sk->sk_protocol     = protocol;
  37.     sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;

  38.     inet->uc_ttl    = -1;
  39.     inet->mc_loop    = 1;
  40.     inet->mc_ttl    = 1;
  41.     inet->mc_all    = 1;
  42.     inet->mc_index    = 0;
  43.     inet->mc_list    = NULL;

  44.     sk_refcnt_debug_inc(sk);

  45.     if (inet->num) {
  46.         /* It assumes that any protocol which allows
  47.          * the user to assign a number at socket
  48.          * creation time automatically
  49.          * shares.
  50.          */
  51.         inet->sport = htons(inet->num);
  52.         /* Add to protocol hash chains. */
  53.         sk->sk_prot->hash(sk);
  54.     }

  55.     if (sk->sk_prot->init) {
  56.         err = sk->sk_prot->init(sk);
  57.         if (err)
  58.             sk_common_release(sk);
  59.     }
  60. }
首先将sock->state设置为未连接状态SS_UNCONNECTED,然后根据用户指定的type()在inetsw查找对应的inet_protosw结构。
这里需要先确认一下inetsw里面对应了哪些内容以及使如何注册上的,现在可以回头看看inet_init了,本文的最上面部分代码,先遍历inetsw_array数组,然后对每个元素调用inet_register_protosw函数进行注册。看看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.         .capability = -1,
  9.         .no_check = 0,
  10.         .flags = INET_PROTOSW_PERMANENT |
  11.              INET_PROTOSW_ICSK,
  12.     },

  13.     {
  14.         .type = SOCK_DGRAM,
  15.         .protocol = IPPROTO_UDP,
  16.         .prot = &udp_prot,
  17.         .ops = &inet_dgram_ops,
  18.         .capability = -1,
  19.         .no_check = UDP_CSUM_DEFAULT,
  20.         .flags = INET_PROTOSW_PERMANENT,
  21.        },


  22.        {
  23.      .type = SOCK_RAW,
  24.      .protocol = IPPROTO_IP,    /* wild card */
  25.      .prot = &raw_prot,
  26.      .ops = &inet_sockraw_ops,
  27.      .capability = CAP_NET_RAW,
  28.      .no_check = UDP_CSUM_DEFAULT,
  29.      .flags = INET_PROTOSW_REUSE,
  30.        }
  31. };
对应了常用的三种类型,(SOCK_STREAM/IPPROTO_TCP), (SOCK_DGRAM/IPPROTO_UDP), (SOCK_RAW/IPPROTO_IP),用户通过type指定。上面的遍历就比较明显了分别把这三种type进行注册,看看注册到哪里去了。

点击(此处)折叠或打开

  1. void inet_register_protosw(struct inet_protosw *p)
  2. {
  3.     ......
  4.     answer = NULL;
  5.     last_perm = &inetsw[p->type];
  6.     list_for_each(lh, &inetsw[p->type]) {
  7.         answer = list_entry(lh, struct inet_protosw, list);

  8.         /* Check only the non-wild match. */
  9.         if (INET_PROTOSW_PERMANENT & answer->flags) {
  10.             if (protocol == answer->protocol)
  11.                 break;
  12.             last_perm = lh;
  13.         }
  14.         answer = NULL;
  15.     }
  16. }
很明显,注册到了inetsw中,现在可以回头继续分析inet_create函数了,answer实际对应了TCP/UDP/RAW的inet_protosw结构,进行一系列赋值,然后调用sk_alloc函数。

点击(此处)折叠或打开

  1. struct sock *sk_alloc(struct net *net, int family, gfp_t prioritystruct proto *prot)
  2. {
  3.     struct sock *sk;

  4.     sk = sk_prot_alloc(prot, priority | __GFP_ZERO, family);
  5.     if (sk) {
  6.         sk->sk_family = family;
  7.         sk->sk_prot = sk->sk_prot_creator = prot;
  8.         sock_lock_init(sk);
  9.         sock_net_set(sk, get_net(net));
  10.         atomic_set(&sk->sk_wmem_alloc, 1);
  11.     }
  12.     return sk;
  13. }
sk_prot_alloc实际上是在prot对应的slab中或者直接kmalloc申请一个sock结构,然后设置协议簇,以及proto结构信息。这里需要注意一下,目前返回的是一个struct sock的结构,而实际上并不仅仅是这个简单的结构。我们先回到inet_create函数中继续向下分析。可以看到申请完sock之后,inet_create函数中使用了inet = inet_sk(sk)对sock进行了强制转换,然后对转换得到的inet结构进行一系列赋值。inet_sock是INET域专用的一个sock表示,在struct sock上扩展而来,除基本sock属性,提供了INET域专用的属性,如TTL、IP地址、端口等。这里是怎么对应上的呢。还得再回头研究一下,申请sock的最终实现,申请sock时最终走的是下面的函数,前面简单提到过,但没深入。

点击(此处)折叠或打开

  1. static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priorityint family)
  2. {
  3.     struct sock *sk;
  4.     struct kmem_cache *slab;

  5.     slab = prot->slab;
  6.     if (slab != NULL) {
  7.         .......
  8.     else
  9.         sk = kmalloc(prot->obj_size, priority);
sock的申请是通过kmalloc实现的,申请的大小由prot->obj_size指定,而prot->obj_size是由上面static struct inet_protosw inetsw_array[]数组成员结构中的proto结构中指定的,而这个Proto结构区分了TCP/UDP/RAW协议,见上面数组定义。其中tcp_prot的.obj_size = sizeof(struct tcp_sock), 
udp_prot的.obj_size   = sizeof(struct udp_sock), raw_prot的.obj_size   = sizeof(struct raw_sock)。再看一下这几个结构的具体定义:

点击(此处)折叠或打开

  1. struct tcp_sock {
  2.     /* inet_connection_sock has to be the first member of tcp_sock */
  3.     struct inet_connection_sock    inet_conn;
  4.     u16    tcp_header_len;    /* Bytes of tcp header to send        */
  5.     u16    xmit_size_goal_segs; /* Goal for segmenting output packets */

  6. /*
  7.  *    Header prediction flags
  8.  *    0x5?10 << 16 + snd_wnd in net byte order
  9.  */
  10.     __be32    pred_flags;
  11. ........
  12. struct udp_sock {
  13.     /* inet_sock has to be the first member */
  14.     struct inet_sock inet;
  15.     int         pending;    /* Any pending frames ? */
  16.     unsigned int     corkflag;    /* Cork is required */
  17.       __u16         encap_type;    /* Is this an Encapsulation socket? */
  18.     /*
  19. ......
  20. struct raw_sock {
  21.     /* inet_sock has to be the first member */
  22.     struct inet_sock inet;
  23.     struct icmp_filter filter;
  24. };
除了tcp_sock第一个对应的是inet_connection_sock外,udp和raw对应的都是inet_sock。我们看一下inet_connection_sock

点击(此处)折叠或打开

  1. struct inet_connection_sock {
  2.     /* inet_sock has to be the first */
  3.     struct inet_sock     icsk_inet;
  4.     struct request_sock_queue icsk_accept_queue;
  5.     struct inet_bind_bucket     *icsk_bind_hash;
  6.     unsigned long         icsk_timeout;
  7.     ......

点击(此处)折叠或打开

  1. struct inet_sock {
  2.     /* sk and pinet6 has to be the first two members of inet_sock */
  3.     struct sock        sk;
  4.     ......
inet_connection_sock第一个成员也是inet_sock,而inet_sock的第一个成员是struct sock。这样就比较清晰了,可以这么理解struct sock是一个基类是Linux socket的最基础的面向协议栈的结构,而inet_sock在继承sock的基础上进行了扩展,除了基本socket属性外提供了INET域专用的属性,如TTL等。简单的,udp_sock和raw_sock在inet_sock的基础上进行了私有扩展。inet_connection_sock在继承inet_sock的基础上,扩展了所有面向连接相关的一些协议信息,而tcp_sock则在inet_connection_sock基础上进一步扩展,增加了TCP的一些私有属性,比如滑动窗口,拥塞控制等。至此,上述inet_create函数中inet = inet_sk(sk);的转换就没有任何疑问了。

回到inet_create,除了对inet_sk结构的赋值外,也对struct sock结构进行了一系列的初始化,主要涉及收发包队列/缓冲区等。最后调用具体协议类型init函数,这里主要是对应了tcp_v4_init_sock的函数,而udp没有定义init,raw的init函数仅仅是把filter字段给清0了。


阅读(3861) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~