原文出处:

 

最近一直在看linux(2.6.32)ipv4部分的代码。之前对linux/net目录下的代码一直没有头绪,不知从何处下手。现在稍微理了一下,觉得有必要做个记录。看代码之前先理清思路是必要的,linux的代码一坨坨,如果没有漫无目的的看,估计就像看天书一样了。我的目的很明确,就是先了解ipv4,其他所有和其相关的模块能忽略就忽略,比较关键的支撑模块也没有必要一定要看他的实现,从其他方面途径(google or anything else)了解用途即可。代码写了一堆往往是为了实现一个简单的事情,但需要考虑的异常处理有很多,所以代码就变得复杂。 

之前对tcp/ip只有理论上的认识,所以要想更深入的了解网络部分代码的原委,也只好捏着鼻子看源代码了,看代码是痛苦的,但看懂以后是愉快的,嘿嘿。
对于一个模块,我想还是先从init开始吧,任何模块都应该从这里开始,i think。
af_inet.c/inet_init()
这是一个静态函数,linux的初始化的确搞的与众不同,他已经把代码的灵活性、扩展性发挥的淋漓尽致,你找这个函数的调用关系,根本没有!
其实有关初始化的猫腻都藏在/include/linux/init.h这个文件里面,如果分析这个文件,可能有点离题,网上应该有不少相关的分析,我才懒得在写,这不符合我看代码的习惯,大概简单一句话的说就是把xx_initcall修饰的函数放入特殊的代码段中(init text segment?),有8个优先级,便于初始化顺序关系的处理,然后在/init/main.c/do_initcalls()中调用。这是预编译指令,编译器,链接器共同协作的结果。有兴趣的可以自己研究。正好我之前调试kernel的时候把这部分的调试开关打开了,我顺便贴在后面,便于查阅。
fs_initcall(inet_init);
__init指示编译器(gcc)把这个函数发在特殊的代码段里,
static int __init inet_init(void)
{
struct sk_buff *dummy_skb;
struct inet_protosw *q;
struct list_head *r;
int rc = -EINVAL;
BUILD_BUG_ON(sizeof(struct inet_skb_parm) > sizeof(dummy_skb->cb));
//proto_register这个函数实现了list_add的功能,不信自己在里面找找。所以这个函数也是可以一句话说清楚的。它的表头是proto_list,真正用到它的,就是遍历他的地方,自己找吧:) 看看/proc/net/protocols就明白了
rc = proto_register(&tcp_prot, 1); //tcp的
if (rc)
goto out;
rc = proto_register(&udp_prot, 1);//udp的
if (rc)
goto out_unregister_tcp_proto;
rc = proto_register(&raw_prot, 1);//raw的,相信大部分人都用过这个吧
if (rc)
goto out_unregister_udp_proto;
/*
* Tell SOCKET that we are alive...
*/
(void)sock_register(&inet_family_ops
);//注册一个socket类型,要注意这个实现可不是链表,是一个数组,因为这是一个可确定的东西,有固定的长度,还记得我们在写c程序时的socket()吧,其中第一个参数一般都会写AF_INET,我们在这个文件中注册的就是这个函数了,如果没有注册这个socket,kernel可不鸟你:),你调用了socket(),通过系统调用(x86 cpu 为int xx指令),到了kernel就变成了sock_create函数了(这个过程写起来也够一遍小文,这里就不费劲了),它根据你传递的第一个参数找到了应该调用那个socket组下的函数指针,传错了可不行,你不能想用INET的功能,却传入PF_NETLINK。扯得有点多,继续往下。
#ifdef CONFIG_SYSCTL //参考/proc/sys/net/ipv4
ip_static_sysctl_init();
#endif
下面这部分相当有用,实现了tcp/ip的报文接收处理的挂接,inet_add_protocol这个函数实现的是数组的赋值,ps:请原谅我说的这么直白,说白了干的就是这个活。到里面看一下这个函数干的活实在是太简单了,一句话就是一个等号完事。简单归简单,接收这个值的全局变量就不简单了,这个全局变量inet_protos被多个地方使用,其中最重要的地方应该是ip_local_deliver_finish用的了,他通过这个变量实现了ip层报文向上层传递的功能(如果我不手懒,能坚持下去的话,会分析这个函数滴)。你是tcp报文,那么调用tcp_protocol.tcp_v4_rcv实现向上传递,如果是udp的报文,则同理。至于调用哪个,相信了解ip头的人都很清楚了。
有对×××感兴趣的没,看看这个
Tunnel4.c (net\ipv4): if (inet_add_protocol(&tunnel4_protocol, IPPROTO_IPIP)) :)

/*

* Add all the base protocols.
*/
if(inet_add_protocol(&icmp_protocol,IPPROTO_ICMP)< 0
printk(KERN_CRIT "inet_init: Cannot add ICMP protocol\n");
if(inet_add_protocol(&udp_protocol,IPPROTO_UDP)< 0)
printk(KERN_CRIT "inet_init: Cannot add UDP protocol\n");
if(inet_add_protocol(&tcp_protocol,IPPROTO_TCP)< 0)
printk(KERN_CRIT "inet_init: Cannot add TCP protocol\n");
#ifdef CONFIG_IP_MULTICAST
if(inet_add_protocol(&igmp_protocol,IPPROTO_IGMP)< 0)
printk(KERN_CRIT "inet_init: Cannot add IGMP protocol\n");
#endif

/* Register the socket-side information for inet_create.*/

for(= &inetsw[0]; r < &inetsw[SOCK_MAX];++r)
INIT_LIST_HEAD(r);//先初始化链表
for(= inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN];++q)
inet_register_protosw(q);//在初始化实体,这里面挂接了socket编程中用到的函数指针和对应协议中用到的函数指针。应该是对sock_register的序。

/*
* Set the ARP module up
*/
arp_init();

void __init arp_init(void) //IPV4的实现离不开arp,这里是对他的初始化

{
neigh_table_init(&arp_tbl);//这个里面做了很多事情,初始化neigh_table结构,挂接函数指针和设置arp需要的参数。neighbour是一个复杂的系统,估计又有一篇小文了。。
dev_add_pack(&arp_packet_type);//添加arp报文的处理,这里不同于inet_add_protocol,因为不是同一层的协议。这个是注册二层报文解析的handle,而inet_add_protocol是注册对三层报文解析的handle。如果你想自己独创一套三层报文协议,那么就调用dev_add_pack添加你的实现就可以了。呵呵,有难度哦。

arp_proc_init(); //参考/proc/net/arp

#ifdef CONFIG_SYSCTL //参考/proc/sys/net/ipv4/...
neigh_sysctl_register(NULL,&arp_tbl.parms, NET_IPV4,
NET_IPV4_NEIGH,"ipv4",NULL,NULL);
#endif
register_netdevice_notifier(&arp_netdev_notifier); //注册对设备状态变化的处理,arp协议只处理了ip地址变化的事件NETDEV_CHANGEADDR,他的处理也很简单,清空对应ip地址的接口的arp表项
}

/*
* Set the IP module up
*/
ip_init();

void __init ip_init(void)

{
ip_rt_init();//由于接收,发送的部分只要挂接指针就可以了,这里面的初始化涉及路由系统的初始化,初始化路由表项以及想proc文件系统注册文件等。
inet_initpeers();//
#if defined(CONFIG_IP_MULTICAST)&& defined(CONFIG_PROC_FS)
igmp_mc_proc_init();//参考/proc/net/igmp
#endif
}

tcp_v4_init(); 
/* Setup TCP slab cache for open requests. */
tcp_init(); //初始化TCP协议用到的链表,设置tcp的默认参数
/* Setup UDP memory threshold */
udp_init(); //初始化TCP协议用到的链表,设置udp的默认参数
/* Add UDP-Lite (RFC 3828) */
udplite4_register(); //实现轻量级UDP协议,不同于普通的UDP协议。
/*
* Set the ICMP layer up
*/
if (icmp_init() < 0) //内核创建raw socket用于处理icmp报文。
panic("Failed to create the ICMP control socket.\n");
/*
* Initialise the multicast router
*/
#if defined(CONFIG_IP_MROUTE)
if (ip_mr_init())
printk(KERN_CRIT "inet_init: Cannot init ipv4 mroute\n");
#endif
/*
* Initialise per-cpu ipv4 mibs
*/
if (init_ipv4_mibs()) //MIB支持?懒得看
printk(KERN_CRIT "inet_init: Cannot init ipv4 mibs\n");
ipv4_proc_init(); // proc/net下一些相关文件
ipfrag_init(); //ip分片的默认参数,及proc接口。/pros/sys/net/ipfrag_*
dev_add_pack(&ip_packet_type); 
//这里添加了对二层数据解析的支持,同arp
rc = 0;
out:
return rc;
out_unregister_udp_proto:
proto_unregister(&udp_prot);
out_unregister_tcp_proto:
proto_unregister(&tcp_prot);
goto out;
}
由此可见,inet的初始化并不简单,但总体来说依赖外部模块的部分还不多,但内部还是错综复杂的。根据这个初始化的线索向下分析思路会清晰的多。根据初始化的流程,后续的分析思路可以是这样:
socket编程接口实现 sock_register(&inet_family_ops);
icmp报文接收/发送处理 inet_add_protocol(&icmp_protocol
udp报文接收/发送处理 inet_add_protocol(&udp_protocol
tcp报文接收/发送处理 inet_add_protocol(&tcp_protocol
arp的实现 arp_init
路由系统实现 ip_init
tcp的实现 这个复杂的很,看代码都能看吐了
第一次写blog,抛砖引玉。每一个课题都不小,不过不急,时间还长,嘿嘿。待续>>>
==================================================================================
附一: init_call 调用顺序,不同的kernel配置结果会不同。
Calling initcall 0xc000ec50: ptrace_break_init+0x0/0x2c()
Calling initcall 0xc000fb84: consistent_init+0x0/0xd0()
Calling initcall 0xc00122fc: sysctl_init+0x0/0x24()
Calling initcall 0xc0012c2c: helper_init+0x0/0x40()
Calling initcall 0xc0012f00: init_jiffies_clocksource+0x0/0x1c()
Calling initcall 0xc0013010: pm_init+0x0/0x40()
Calling initcall 0xc00130b0: ksysfs_init+0x0/0x40()
Calling initcall 0xc00150ac: filelock_init+0x0/0x54()
Calling initcall 0xc0015cc0: init_script_binfmt+0x0/0x1c()
Calling initcall 0xc0015cdc: init_elf_binfmt+0x0/0x1c()
Calling initcall 0xc001720c: random32_init+0x0/0x2c()
Calling initcall 0xc001aed4: sock_init+0x0/0x88()
Calling initcall 0xc001b7c4: netlink_proto_init+0x0/0x174()
Calling initcall 0xc00170ec: kobject_uevent_init+0x0/0x54()
Calling initcall 0xc00172c0: pcibus_class_init+0x0/0x1c()
Calling initcall 0xc00178c4: pci_driver_init+0x0/0x1c()
Calling initcall 0xc0017dd4: tty_class_init+0x0/0x3c()
Calling initcall 0xc00187d4: vtconsole_class_init+0x0/0xdc()
Calling initcall 0xc000eea8: customize_machine+0x0/0x2c()
Calling initcall 0xc000eed4: topology_init+0x0/0x30()
Calling initcall 0xc0011368: ixdp425_pci_init+0x0/0x38()
Calling initcall 0xc001140c: wgrdv2_pci_init+0x0/0x20()
Calling initcall 0xc00129d4: param_sysfs_init+0x0/0x194()
Calling initcall 0xc005baf8: pm_sysrq_init+0x0/0x24()
Calling initcall 0xc00158dc: init_bio+0x0/0x148()
Calling initcall 0xc0016f58: genhd_device_init+0x0/0x64()
Calling initcall 0xc00181d8: misc_init+0x0/0x9c()
Calling initcall 0xc0019d90: phy_init+0x0/0x38()
Calling initcall 0xc001a0c8: usb_init+0x0/0x11c()
Calling initcall 0xc001a830: serio_init+0x0/0x98()
Calling initcall 0xc001a900: input_init+0x0/0x120()
Calling initcall 0xc001abb0: rtc_init+0x0/0x58()
Calling initcall 0xc001ac08: rtc_sysfs_init+0x0/0x1c()
Calling initcall 0xc001ac24: rtc_proc_init+0x0/0x1c()
Calling initcall 0xc001ac40: rtc_dev_init+0x0/0xc0()
Calling initcall 0xc001ad20: i2c_init+0x0/0x4c()
Calling initcall 0xc001ae2c: hwmon_init+0x0/0x50()
Calling initcall 0xc001ae7c: leds_init+0x0/0x3c()
Calling initcall 0xc001afd8: proto_init+0x0/0x48()
Calling initcall 0xc001b178: net_dev_init+0x0/0x1e4()
Calling initcall 0xc001b5dc: wireless_nlevent_init+0x0/0x2c()
Calling initcall 0xc001b650: fib_rules_init+0x0/0x1c()
Calling initcall 0xc001b66c: pktsched_init+0x0/0xb4()
Calling initcall 0xc001b73c: tc_filter_init+0x0/0x44()
Calling initcall 0xc001b780: tc_action_init+0x0/0x44()
Calling initcall 0xc001b938: genl_init+0x0/0xc4()
Calling initcall 0xc0010f20: alignment_init+0x0/0x94()
Calling initcall 0xc0012ddc: clocksource_done_booting+0x0/0x24()
Calling initcall 0xc001500c: init_pipe_fs+0x0/0x54()
Calling initcall 0xc0017ad4: chr_dev_init+0x0/0x94()
Calling initcall 0xc001c548: inet_init+0x0/0x39c()
Calling initcall 0xc000e508: populate_rootfs+0x0/0xec()
Calling initcall 0xc000f728: timer_init_sysfs+0x0/0x90()
Calling initcall 0xc00115d4: fpe_init+0x0/0x84()
Calling initcall 0xc0011dc8: create_proc_profile+0x0/0x74()
Calling initcall 0xc00122a0: ioresources_init+0x0/0x5c()
Calling initcall 0xc0012320: timekeeping_init_device+0x0/0x38()
Calling initcall 0xc0012610: uid_cache_init+0x0/0xb0()
Calling initcall 0xc0012b68: init_posix_timers+0x0/0xc4()
Calling initcall 0xc0012c6c: init_posix_cpu_timers+0x0/0xe8()
Calling initcall 0xc0012da0: latency_init+0x0/0x3c()
Calling initcall 0xc0012e98: init_clocksource_sysfs+0x0/0x68()
Calling initcall 0xc0012f1c: init_timer_list_procfs+0x0/0x40()
Calling initcall 0xc0012f5c: init+0x0/0x7c()
Calling initcall 0xc0012fd8: kallsyms_init+0x0/0x38()
Calling initcall 0xc0013050: ikconfig_init+0x0/0x60()
Calling initcall 0xc0013280: utsname_sysctl_init+0x0/0x20()
Calling initcall 0xc0013d64: init_per_zone_pages_min+0x0/0x50()
Calling initcall 0xc0014684: pdflush_init+0x0/0x28()
Calling initcall 0xc00146e4: kswapd_init+0x0/0x34()
Calling initcall 0xc001478c: procswaps_init+0x0/0x38()
Calling initcall 0xc00147c4: init_tmpfs+0x0/0xec()
Calling initcall 0xc00148d4: cpucache_init+0x0/0x1c()
Calling initcall 0xc0015060: fasync_init+0x0/0x4c()
Calling initcall 0xc00157d0: aio_setup+0x0/0x98()
Calling initcall 0xc0015acc: inotify_setup+0x0/0x24()
Calling initcall 0xc0015af0: inotify_user_setup+0x0/0xf0()
Calling initcall 0xc0015be0: eventpoll_init+0x0/0xe0()
Calling initcall 0xc0015cf8: init_mbcache+0x0/0x30()
Calling initcall 0xc0015d28: dnotify_init+0x0/0x4c()
Calling initcall 0xc0016234: init_devpts_fs+0x0/0x48()
Calling initcall 0xc001627c: init_ext3_fs+0x0/0x90()
Calling initcall 0xc0016404: journal_init+0x0/0xf8()
Calling initcall 0xc00164fc: init_ext2_fs+0x0/0x80()
Calling initcall 0xc0016598: init_ramfs_fs+0x0/0x1c()
Calling initcall 0xc0016780: init_jffs2_fs+0x0/0xe4()
Calling initcall 0xc0016910: ipc_init+0x0/0x20()
Calling initcall 0xc0016bb0: ipc_sysctl_init+0x0/0x20()
Calling initcall 0xc0016bd0: init_mqueue_fs+0x0/0xe4()
Calling initcall 0xc0016cb4: crypto_algapi_init+0x0/0x18()
Calling initcall 0xc0016d00: cryptomgr_init+0x0/0x1c()
Calling initcall 0xc0016d1c: hmac_module_init+0x0/0x1c()
Calling initcall 0xc0016d38: init+0x0/0x1c()
Calling initcall 0xc0016d54: init+0x0/0x1c()
Calling initcall 0xc0016d70: crypto_cbc_module_init+0x0/0x1c()
Calling initcall 0xc0016d8c: init+0x0/0x44()
Calling initcall 0xc0016fbc: noop_init+0x0/0x1c()
Calling initcall 0xc0016fd8: as_init+0x0/0x1c()
Calling initcall 0xc0016ff4: deadline_init+0x0/0x1c()
Calling initcall 0xc0017010: cfq_init+0x0/0xdc()
Calling initcall 0xc01164e4: pci_init+0x0/0x3c()
Calling initcall 0xc00178e0: pci_sysfs_init+0x0/0x50()
Calling initcall 0xc0017930: pci_proc_init+0x0/0x90()
Calling initcall 0xc0017b84: rand_initialize+0x0/0x38()
Calling initcall 0xc0017c00: tty_init+0x0/0x1d4()
Calling initcall 0xc0017e10: pty_init+0x0/0x3c8()
Calling initcall 0xc00188e0: pcf8594c_nvram_init+0x0/0x58()
Calling initcall 0xc0018cf8: serial8250_init+0x0/0x11c()
Calling initcall 0xc0018f18: serial8250_pci_init+0x0/0x28()
Calling initcall 0xc00196e0: rd_init+0x0/0x1c8()
Calling initcall 0xc00198d4: loop_init+0x0/0x290()
Calling initcall 0xc0019b64: nbd_init+0x0/0x22c()
Calling initcall 0xc0019e4c: net_olddevs_init+0x0/0xcc()
Calling initcall 0xc0019f18: loopback_init+0x0/0x1c()
Calling initcall 0xc0019f34: init_mtd+0x0/0x48()
Calling initcall 0xc0019f7c: init_mtdchar+0x0/0x98()
Calling initcall 0xc001a014: init_mtdblock+0x0/0x1c()
Calling initcall 0xc001a030: cfi_probe_init+0x0/0x20()
Calling initcall 0xc001a050: jedec_probe_init+0x0/0x20()
Calling initcall 0xc001a070: ixp4xx_flash_init+0x0/0x1c()
Calling initcall 0xc001a08c: nand_base_init+0x0/0x14()
Calling initcall 0xc001a0a0: wg_nand_init+0x0/0x28()
Calling initcall 0xc001a2d0: mon_init+0x0/0xb4()
Calling initcall 0xc001a428: ehci_hcd_init+0x0/0x18c()
Calling initcall 0xc001a5b4: usb_serial_init+0x0/0x214()
Calling initcall 0xc001a7c8: pl2303_init+0x0/0x68()
Calling initcall 0xc001a8c8: serport_init+0x0/0x38()
Calling initcall 0xc001aa20: mousedev_init+0x0/0xec()
Calling initcall 0xc001ab0c: atkbd_init+0x0/0x28()
Calling initcall 0xc001ab34: psmouse_init+0x0/0x7c()
Calling initcall 0xc001ad00: s35390a_rtc_init+0x0/0x20()
Calling initcall 0xc001ad6c: i2c_dev_init+0x0/0xa4()
Calling initcall 0xc001ae10: ixp4xx_i2c_init+0x0/0x1c()
Calling initcall 0xc001aeb8: ixp4xxgpioled_init+0x0/0x1c()
Calling initcall 0xc001b490: flow_cache_init+0x0/0x14c()
Calling initcall 0xc001b720: blackhole_module_init+0x0/0x1c()
Calling initcall 0xc001cbf8: init_syncookies+0x0/0x24()
Calling initcall 0xc001cc1c: ah4_init+0x0/0x74()
Calling initcall 0xc001cc90: esp4_init+0x0/0x74()
Calling initcall 0xc001cd04: xfrm4_beet_init+0x0/0x20()
Calling initcall 0xc001cd24: xfrm4_transport_init+0x0/0x20()
Calling initcall 0xc001cd44: xfrm4_tunnel_init+0x0/0x20()
Calling initcall 0xc01fba78: ipv4_netfilter_init+0x0/0x1c()
Calling initcall 0xc001cd64: inet_diag_init+0x0/0x80()
Calling initcall 0xc001cde4: tcp_diag_init+0x0/0x1c()
Calling initcall 0xc001ce00: cubictcp_register+0x0/0x88()
Calling initcall 0xc001d10c: xfrm_shim_init+0x0/0x108()
Calling initcall 0xc001d214: xfrm_user_init+0x0/0xa4()
Calling initcall 0xc001d2b8: af_unix_init+0x0/0x80()
Calling initcall 0xc001d338: packet_init+0x0/0x70()
Calling initcall 0xc000fad4: xscale_cp0_init+0x0/0xb0()
Calling initcall 0xc0017238: random32_reseed+0x0/0x34()
Calling initcall 0xc0017b68: seqgen_init+0x0/0x1c()
Calling initcall 0xc0018ff0: early_uart_console_switch+0x0/0x98()
Calling initcall 0xc001c27c: tcp_congestion_default+0x0/0x1c()