写在代码前:
我: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.cline 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不是协议栈的主要主题,不作分析
至此,网络协议初始化就完成了。