写在代码前:
我:Jack,出来吧。今天晚上咱们分析Linux1.2.4内核的协议栈。
Jack:上次不是分析Linux1.0的内核协议栈吗?怎么突然之间要分析1.2.4了?
我:上次分析完Linux1.0的网卡初始化之后,又看了下sock初始化,发现1.0的代码协议栈这块儿层次还不是特别清晰,我甚至不知道它能不能通信。
所以,我选择用1.2.4版本分析,这个版本层次更清晰些。
Jack:好吧。你分析吧。
asmlinkage void start_kernel(void) //init/main.c line 342 sock_init(); //init/main.c line 383 proto_init(); //net/socket.c line 1362 while (pro->name != NULL) { (*pro->init_func)(pro); pro++; } dev_init(); //net/socket.c line 1369
问:在上一篇文章里不是已经做过网卡初始化了吗?为什么还会有dev_init()操作。
答:在上一篇文章是生成dev这个结构体对象,这里的dev_init是执行dev对象你的函数指针.
这里的调用栈最核心的是proto_init()函数。//net/socket.c line 1362
void proto_init(void) { extern struct net_proto protocols[]; /* Network protocols */ struct net_proto *pro; /* Kick all configured protocols. */ pro = protocols; while (pro->name != NULL) { (*pro->init_func)(pro); pro++; } /* We're all done... */ }
这个函数没有参数传入——因为这个函数处理的数据是个全局变量——net_proto protocols[];。(全局变量这种龊B的东西在早期的Linux内核用得挺多的,Linux内核其实真的不神秘)
net_proto protocols[];定义在net/protocols.c
struct net_proto protocols[] = { #ifdef CONFIG_UNIX { "UNIX", unix_proto_init }, #endif #if defined(CONFIG_IPX)||defined(CONFIG_ATALK) { "UNIX", p8022_proto_init }, { "SNAP", snap_proto_init }, #endif #ifdef CONFIG_AX25 { "AX.25", ax25_proto_init }, #endif #ifdef CONFIG_INET { "INET", inet_proto_init }, #endif #ifdef CONFIG_IPX { "IPX", ipx_proto_init }, #endif #ifdef CONFIG_ATALK { "DDP", atalk_proto_init }, #endif { NULL, NULL } };
在我们一般的概念中,网络就是inet。其实inet只是net pro中的一种(用得最多,见得最多的那种)。在1.2.4的Linux内核版本里,支持"UNIX","UNIX","SNAP","AX.25","INET","IPX","DDP"这么多种net协议。而且,每种协议都有其对应的协议初始化函数——其实就是一个函数指针。例如我们知道的tcp/ip的协议初始化函数指针是inet_proto_init。
我们接着看一下inet_proto_init这个初始化函数做了些什么。
void inet_proto_init(struct net_proto *pro) { struct inet_protocol *p; int i; printk("Swansea University Computer Society TCP/IP for NET3.019\n"); /* * Tell SOCKET that we are alive... */ (void) sock_register(inet_proto_ops.family, &inet_proto_ops); seq_offset = CURRENT_TIME*250; /* * Add all the protocols. */ for(i = 0; i < SOCK_ARRAY_SIZE; i++) { tcp_prot.sock_array[i] = NULL; udp_prot.sock_array[i] = NULL; raw_prot.sock_array[i] = NULL; } tcp_prot.inuse = 0; tcp_prot.highestinuse = 0; udp_prot.inuse = 0; udp_prot.highestinuse = 0; raw_prot.inuse = 0; raw_prot.highestinuse = 0; printk("IP Protocols: "); for(p = inet_protocol_base; p != NULL;) { struct inet_protocol *tmp = (struct inet_protocol *) p->next; inet_add_protocol(p); printk("%s%s",p->name,tmp?", ":"\n"); p = tmp; } /* * Set the ARP module up */ arp_init(); /* * Set the IP module up */ ip_init(); }
这个函数做了3件事:
1、 (void) sock_register(inet_proto_ops.family, &inet_proto_ops); //注册inet协议族,把inet协议的ops挂上去。
2、arp_init()。
3、ip_init()。
inet_proto_ops其实就是一个跳转表。定义在net/inet/af_inet.c line 1482
static struct proto_ops inet_proto_ops = { AF_INET, inet_create, inet_dup, inet_release, inet_bind, inet_connect, inet_socketpair, inet_accept, inet_getname, inet_read, inet_write, inet_select, inet_ioctl, inet_listen, inet_send, inet_recv, inet_sendto, inet_recvfrom, inet_shutdown, inet_setsockopt, inet_getsockopt, inet_fcntl, };
如果你对Linux的文件系统比较熟悉,这些东西应该是一看就明白的。
arp_init();
ip_init();
这两个函数做的事情是完全类似的。
void arp_init (void) { /* Register the packet type */ arp_packet_type.type=htons(ETH_P_ARP); dev_add_pack(&arp_packet_type); /* Start with the regular checks for expired arp entries. */ add_timer(&arp_timer); /* Register for device down reports */ register_netdevice_notifier(&arp_dev_notifier); }
void ip_init(void) { ip_packet_type.type=htons(ETH_P_IP); dev_add_pack(&ip_packet_type); /* So we flush routes when a device is downed */ register_netdevice_notifier(&ip_rt_notifier); /* ip_raw_init(); ip_packet_init(); ip_tcp_init(); ip_udp_init();*/ }
分别做了2件事:
1、定义包类型。
2、注册notifier。
notifier不是协议栈的主要主题,不作分析
至此,网络协议初始化就完成了。