网络子系统初始化--ipv4模块加载时初始化

 

上一篇文章中介绍了socket系统的初始化,下面开始介绍目前应用最广泛的一个协议族:ipv4(或者叫af_inet)的初始化。ipv4作为一个内嵌模块,位于net/ipv4目录下,入口点为inet_init()[net/ipv4/af_inet.c:1512],该函数写的很整齐,方便了我们这些读者。先上代码:

下面分四部分对其进行解读。

1. 首先是三个proto_register(),分别注册了tcp_prot,udp_prot,raw_prot三个协议(struct proto[include/net/sock.h:594])(这三个对象在后面的初始化过程中还会涉及到,可以比较一下出现这两次的目的何在),跟到proto_register()[net/core/sock.c:2041]中看一下,首先是一个大的if,为这个proto分配了几个必要的slab。之后做了什么呢?

 

只是把它加到了全局变量proto_list[net/sock/sock.c:1937]中,这个链表在内核里又起什么作用呢?因为proto_list是静态全局变量,也就是说只有在该文件中才能被访问到,所以不妨在该文件中对其搜索一下:经过搜索发现,proto_list除了在proto_register和proto_unregister中被使用到,就只有在proc_fs中被使用,也就是输出目前proto_list链表中所有节点的状态。

 

2. sock_register()[net/socket.c:2151]。该函数的实现也很简单:向net_families[net/socket.c:150]中注册一个新的协议族(struct net_proto_family[include/linux/net.h:192])。协议族是linux网络模块划分的最大单位,每个协议族都被分配了一个协议族号(在include/linux/socket.h:158~232中定义),从该文件中可以看出目前最大的协议族号只有35,即AF_PHONET。既然协议族的数量很少,那么net_families就采用了一种最高效的数据结构:数组。35个net_proto_family的空间都是预分配的,只有在注册时才会向该协议族自己的位置上增加对应的family,create和owner。

 

到此为止,af_inet对上层的初始化操作基本完成,在此可以看到一个网络协议(确切的说是协议族)模块初始化的一般流程。两个步骤:proto_register()然后sock_register()。先注册proto,然后注册net_proto_family。每个协议族至少包含一个协议,如协议族netlink(net/netlink/af_netlink)就只有一个协议netlink。

下面的初始化操作就和上层没有多大关系了,主要是af_inet内部协议的初始化。

 

3. inet_add_protocol()[net/ipv4/protocol.c:53]。向af_inet注册一个协议,注意此时的注册的协议为struct net_protocol[include/net/protocol.h:36]。inet_add_protocol()的实现很简单,只是将协议设置到静态变量inet_protos[net/ipv4/protocol.c:46]数组中。inet_protos的声明有些奇怪,下面我们来看一下

 

后面加了一个____cacheline_aligned_in_smp,根据它的名字可以看出,它的作用是在多处理器环境下对齐cacheline,而cacheline又是什么东西呢?可以参考http://blog.csdn.net/pennyliang/archive/2010/10/20/5953939.aspx,一位牛人的blog,这篇文章大致介绍了一下为什么cacheline aligned对齐能够在多处理器环境下加速。

inet_protos在include/net/protocol.h:98中被extern,那这样的话我们只有对整个内核进行grep了(也可以使用网上的lxr,但只恐找不全或内核版本不匹配),经过grep,可以确定该变量只在net/ipv4/目录下的af_inet.c,icmp.c,ip_input.c,protocol.c四个文件中被使用,且前三个文件中只是使用rcu_dereference()对数组中的某一个协议进行引用,协议的增加和删除都在protocol.c中。

TODO:至于这些协议是怎么被上层调用到的,目前还不清楚。

现在请关注该函数的第二个参数:protocol_num。这是af_inet中协议的协议号,这些协议号都是向一个名叫iana[http://www.iana.org]的组织申请的,而且这个组织不光分配af_inet的协议号,af_inet的协议也是由他们制定的,制定完善后的协议会变成RFC发布到ietf[http://www.ietf.org]。iana还拥有全球域名分配和IP地址分配的大权。已注册协议号可以在http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml中看到。

 

4. inet_register_protosw()[net/ipv4/af_inet.c:967]。向inetsw[net/ipv4/af_inet.c:124]注册一个struct inet_protosw,这个结构体很简单:

 

成员type和protocol分别指代socket类型,协议号。socket类型即BSD socket中标准的几个类型,像SOCK_STREAM,SOCK_DGRAM,SOCK_RAW等等;协议号就是第三节中所提到的af_inet的协议号。

下面两个成员是两组函数接口:prot和ops。我们可以发现,prot就是在第一节proto_register()中注册的协议,可见,这层协议是给上层使用的;ops这一套函数接口在之后就会发现它位于prot下方。

现在可以明白struct inet_protosw的作用了:它的目的就是承上启下,起到衔接BSD socket接口和af_inet协议族接口的作用。

再来看inetsw这个全局变量,它又是做什么作用的呢?grep看一下,很明显,除了在inet_register_protosw注册的使用使用到该变量以外,就只有在inet_create()[net/ipv4/af_inet.c:265]函数中使用了。我们现在只要知道,inet_create()是af_inet在创建socket时候的总入口点(下一章会对其进行详细剖析),那这就很明白了,inetsw就是为inet_create服务,在创建socket的时候方便它找到协议接口,正如inetsw声明时所说:"The inetsw table contains everything that inet_create needs to build a new socket."

 

说到inetsw,就不得不提及inetsw_array[net/ipv4/af_inet.c:930],inetsw就是根据它初始化的,可以看到这里定义了TCP,UDP和IP三个协议的inet_protosw类型。值得关注的是IP的protocol字段,它设置的IPPROTO_IP宏的值为0,我们再到iana查一下这个协议号,iana上显示这个协议是"IPv6 Hop-by-Hop Option"!为什么会不一致呢?下面说一下我的理解:

我们知道在TCP/IP协议中,IP位于L3网络层,TCP,UDP位于L4传输层,BSD socket所用到的protocol应该是指L4的协议,那IP协议当然不在其中。但BSD socket的SOCK_RAW类型该如何实现呢?所以就有了这个IPPROTO_IP,在注释中有这样对它的一个形容:"wild card",直译是百搭牌,可以指代任何类型的协议。实现SOCK_RAW的难度太大,只有把这张牌打出来了...百搭的体现可以在后面对inet_create()的剖析中找到。SOCK_RAW这种类型目前我还没有怎么接触,不好做评价。

 

 

 

接下来调用的几个初始化函数都是和具体的protocol相关的,介绍到具体的protocol时再具体释义。下一章我们会从af_inet协议族的inet_create方法入手,是它引出了所有后面的故事。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值