TCP/IP协议栈初始化(四) 向下走,找到IP向上进阶的入口

  目前OS已经知道inet协议族的存在了,但inet自己知道IP层还没有准备好,它还不能为OS服务。
  继续看inet_init函数。下面的代码就有趣了。
1386     if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
1387         printk(KERN_CRIT "inet_init  Cannot add ICMP protocol\n");
1388     if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
1389         printk(KERN_CRIT "inet_init  Cannot add UDP protocol\n");
1390     if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
1391         printk(KERN_CRIT "inet_init  Cannot add TCP protocol\n");
1386-1391 三个同样的函数,增加三个protocol。从函数的名称来看,是向inet增加的。从之前的分析来看,我们的inet已经向OS报到了。光是OS知道inet还是不够的,inet下面有一帮小弟,这些小弟现在还没有挂靠到inet,inet还是无法工作的。所以就有了这个函数的三次调用,添加三个小弟.UDP,TCP,ICMP。那么问题又来了,为什么ICMP和TCP、UDP并列了呢?熟悉协议栈的同学一定知道,ICMP和IP协议是一对好基友。IP传输过程中出现了什么状况,都是靠ICMP打小报告,通过上层大哥(TCP UDP)的。把它和TCP UPD并列显然是没有道理的。不过仔细一想,也有道理。ICMP报文是通过什么协议传输的?当然是IP协议了,也就是说ICMP和UDP TCP一样,也是要使用IP协议的,这样一想ICMP虽然为IP协议服务,却也享受了IP协议的服务,果然一对好基友不是!这样说的话,在inet中,ICMP和UDP TCP一样位于IP协议层之上,所以先要准备好ICMP UDP TCP。步子一次一步,别迈太大。
  我好奇的是如果把三个协议和inet关联起来的。走去看下tcp_protocol(来自于同一个文件net/ipv4/af_inet.c)的内容,然后看下inet_add_protocol干了什么事情。
  tcp_protocol比其他两个协议内容多一些,如下。如果说,现在在一步步向下打通整个协议栈,你可能不信。不过事实就是如此,linux的世界里秩序是非常重要的,一切复杂的东西,都是以简单的道理开始。目前来看,tcp_protocol就是连接inet和IP层的接口。tcp_protocol中的handler被赋值为tcp_v4_rcv,而tcp_v4_rcv函数的原型为 int tcp_v4_rcv(struct sk_buff *skb)。看到了熟悉的sk_buff,也就明白了来龙去脉了,这里真的是IP层向上走的入口了。其他成员暂不提。再去看看inet_add_protocol函数。
1273 static struct net_protocol tcp_protocol = {
1274     .handler =    tcp_v4_rcv,
1275     .err_handler =    tcp_v4_err,
1276     .gso_send_check = tcp_v4_gso_send_check,
1277     .gso_segment =    tcp_tso_segment,
1278     .no_policy =    1,
1279 };
  inet_add_protocol函数来自于net/ipv4/protocol.c文件中。这个函数很简单,就喜欢这种简单的函数。我们关键点在65行。把传入的prot赋值给inet_protos对应的位置。inet_protos定义的一个数组,用来记录注册好的协议地址。显然这个数组是经常访问的,要求编译器缓存对齐。通过这个数组,加上我们的协议号,inet就能找到对应的协议数据结构,如上所述的tcp_protocol。
48 struct net_protocol *inet_protos[MAX_INET_PROTOS] ____cacheline_aligned_in_smp;
55 int inet_add_protocol(struct net_protocol *prot, unsigned char protocol)
56 {
57     int hash, ret;
59     hash = protocol & (MAX_INET_PROTOS - 1);
61     spin_lock_bh(&inet_proto_lock);
62     if (inet_protos[hash]) {
63         ret = -1;
64     } else {
65         inet_protos[hash] = prot;
66         ret = 0;
67     }
68     spin_unlock_bh(&inet_proto_lock);
70     return ret;
71 }
  面向IP层的接口已经注册好了。下面回来inet_init函数中。
1398     for (r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
1399         INIT_LIST_HEAD(r);
1401     for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
1402         inet_register_protosw(q);
1398-1399 初始化了inetsw数组中的各个元素。
1401-1402 代码中的注释说,是为inet_create提供socket侧的信息。我们先去看下inetsw_array是什么。定义在同一个文件af_inet.c中。这个数组有三个成员。不难看出,这就是这面提到过的3个控制层的协议。看到前面说过的熟悉的tcp_prot就明白了,tcp_prot果然是给socket用的。其他个成员就不说了。
  去看下inet_register_protosw这个函数。
910 static struct inet_protosw inetsw_array[] =
911 {
912     {
913         .type =       SOCK_STREAM,
914         .protocol =   IPPROTO_TCP,
915         .prot =       &tcp_prot,
916         .ops =        &inet_stream_ops,
917         .capability = -1,
918         .no_check =   0,
919         .flags =      INET_PROTOSW_PERMANENT |
920                   INET_PROTOSW_ICSK,
921     },
923     {
924         .type =       SOCK_DGRAM,
925         .protocol =   IPPROTO_UDP,
926         .prot =       &udp_prot,
927         .ops =        &inet_dgram_ops,
928         .capability = -1,
929         .no_check =   UDP_CSUM_DEFAULT,
930         .flags =      INET_PROTOSW_PERMANENT,
931        },
934        {
935            .type =       SOCK_RAW,
937            .prot =       &raw_prot,
938            .ops =        &inet_sockraw_ops,
939            .capability = CAP_NET_RAW,
940            .no_check =   UDP_CSUM_DEFAULT,
941            .flags =      INET_PROTOSW_REUSE,
942        }
943 };
  函数的定义还是在这个文件里。这个函数很简单,因为我们第一次执行时,inetsw数组里的成员都是空的。直接跳到983行执行。把对应的inet_protosw的list成员加入到对应的inetsw的成员列表头中。至此inetsw_array中的成员全部挂靠在inetsw中了。
void inet_register_protosw(struct inet_protosw *p)
948 {
949     struct list_head *lh;
950     struct inet_protosw *answer;
951     int protocol = p->protocol;
952     struct list_head *last_perm;
954     spin_lock_bh(&inetsw_lock);
956     if (p->type >= SOCK_MAX)
957         goto out_illegal;
960     answer = NULL;
961     last_perm = &inetsw[p->type];
962     list_for_each(lh, &inetsw[p->type]) {
963         answer = list_entry(lh, struct inet_protosw, list);
966         if (INET_PROTOSW_PERMANENT & answer->flags) {
967             if (protocol == answer->protocol)
968                 break;
969             last_perm = lh;
970         }
972         answer = NULL;
973     }
974     if (answer)
975         goto out_permanent;
983     list_add_rcu(&p->list, last_perm);

1001 }
  至此可以有一个大概的轮廓了。
  tcp_prot负责tcp_sock数据结构的维护,接收socket层的指令。tcp_prot是放在inetsw数组中。tcp_protocol负责与IP层接洽,tcp_protocol放在inet_protos中。而inetsw,inet_protos都是inet子系统中可见的。
  这当中还是有很多疑惑。比如inet_family_ops的成员函数inet_create是如何调用tcp_prot的成员函数的?socket创建完成后,是不是直接由socket调用tcp_prot的成员函数?tcp层是如何把数据传递给IP层的?虽然这些疑惑暂时未解开,那张图片可以补充上一点内容了。

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值