本文分析基于内核Linux Kernel 1.2.13
以后的系列博文将深入分析Linux内核的网络栈实现原理,这里看到曹桂平博士的分析后,也决定选择Linux内核1.2.13版本进行分析。
原因如下:
1.功能和网络栈层次已经非常清晰
2.该版本与其后续版本的衔接性较好
3.复杂度相对新的内核版本较小,复杂度低,更容易把握网络内核的实质
4.该内核版本比较系统资料可以查询
下面开始零基础分析Linux内核网络部分的初始化过程。
经过系统加电后执行的bootsect.S,setup.S,head.S,可以参考以前分析的0.11内核。原理相同。
进行前期的准备工作后,系统跳转到init/main.c下的start_kernel函数执行。
网络栈的层次结构如下图:(注:该图片摘自《Linux内核网络栈源代码情景分析》)
start_kernel函数经过平台初始化,内存初始化,陷阱初始化,中断初始化,进程调度初始化,缓冲区初始化等,然后执行socket_init(),最后开中断执行init()。
内核的网络战初始化函数socket_init()函数的实现在net/socket.c中
下面是该函数的实现
voidsock_init(void)//网络栈初始化
{
inti;
printk("Swansea University Computer Society NET3.019\n");
/*
* Initialize all address (protocol) families.
*/
for(i = 0; i
/*
* Initialize the protocols module.
*/
proto_init();
#ifdef CONFIG_NET
/*
* Initialize the DEV module.
*/
dev_init();
/*
* And the bottom half handler
*/
bh_base[NET_BH].routine= net_bh;
enable_bh(NET_BH);
#endif
}其中的地址族协议初始化语句for (i = 0; i < NPROTO; ++i) pops[i] = NULL;
这里文件中定义的NPROTO为16
#define NPROTO16/* should be enough for now..*/
而pop[i]是如何定义的呢?
static struct proto_ops *pops[NPROTO];
proto_ops结构体是什么呢?该结构体的定义在include/linux/net.h中,该结构体是具体的操作函数集合,是联系BSD套接字和INET套接字的接口,可以把BSD套接字看做是INET套接字的抽象,结构示意图如下:
具体定义在net.h中
structproto_ops {
intfamily;
int(*create) (structsocket *sock,intprotocol);
int(*dup) (structsocket *newsock,structsocket *oldsock);
int(*release) (structsocket *sock,structsocket *peer);
int(*bind) (structsocket *sock,structsockaddr *umyaddr,
intsockaddr_len);
int(*connect) (structsocket *sock,structsockaddr *uservaddr,
intsockaddr_len,intflags);
int(*socketpair) (structsocket *sock1,structsocket *sock2);
int(*accept) (structsocket *sock,structsocket *newsock,
intflags);
int(*getname) (structsocket *sock,structsockaddr *uaddr,
int*usockaddr_len,intpeer);
int(*read) (structsocket *sock,char*ubuf,intsize,
intnonblock);
int(*write) (structsocket *sock,char*ubuf,intsize,
intnonblock);
int(*select) (structsocket *sock,intsel_type,
select_table *wait);
int(*ioctl) (structsocket *sock, unsignedintcmd,
unsignedlongarg);
int(*listen) (structsocket *sock,intlen);
int(*send) (structsocket *sock,void*buff,intlen,intnonblock,
unsigned flags);
int(*recv) (structsocket *sock,void*buff,intlen,intnonblock,
unsigned flags);
int(*sendto) (structsocket *sock,void*buff,intlen,intnonblock,
unsigned flags,structsockaddr *,intaddr_len);
int(*recvfrom) (structsocket *sock,void*buff,intlen,intnonblock,
unsigned flags,structsockaddr *,int*addr_len);
int(*shutdown) (structsocket *sock,intflags);
int(*setsockopt) (structsocket *sock,intlevel,intoptname,
char*optval,intoptlen);
int(*getsockopt) (structsocket *sock,intlevel,intoptname,
char*optval,int*optlen);
int(*fcntl) (structsocket *sock, unsignedintcmd,
unsignedlongarg);
};可以看到,这里实际上就是一系列操作的函数,有点类似于文件系统中的file_operations。通过参数传递socket完成操作。
接下来是proto_init()协议初始化。
voidproto_init(void)
{
externstructnet_proto protocols[];/* Network protocols 全局变量,定义在protocols.c中 */
structnet_proto *pro;
/* Kick all configured protocols. */
pro = protocols;
while(pro->name != NULL)
{
(*pro->init_func)(pro);
pro++;
}
/* We're all done... */
}全局的protocols定义如下:
structnet_proto protocols[] = {
#ifdef CONFIG_UNIX
{"UNIX", unix_proto_init },
#endif
#if defined(CONFIG_IPX)||defined(CONFIG_ATALK)
{"802.2", 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 }
};而结构体net_proto的定义net.h中为
structnet_proto {
char*name;/* Protocol name */
void(*init_func)(structnet_proto *);/* Bootstrap */
};以后注重讨论标准的INET域
让我们回到proto_init()函数
接下来会执行inet_proto_init()函数,进行INET域协议的初始化。该函数的实现在net/inet/af_inet.c中
其中的
(void) sock_register(inet_proto_ops.family, &inet_proto_ops);
intsock_register(intfamily,structproto_ops *ops)
{
inti;
cli();//关中断
for(i = 0; i
{
if(pops[i] != NULL)
continue;//如果不空,则跳过
pops[i] = ops;//进行赋值
pops[i]->family = family;
sti();//开中断
return(i);//返回用于刚刚注册的协议向量号
}
sti();//出现异常,也要开中断
return(-ENOMEM);
}
参数中的inet_proto_ops定义如下:
staticstructproto_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,
};其中AF_INET宏定义为2,即INET协议族号为2,后面是函数指针,INET域的操作函数。
然后
printk("IP Protocols: ");
for(p = inet_protocol_base; p != NULL;)//将inet_protocol_base指向的一个inet_protocol结构体加入数组inet_protos中
{
structinet_protocol *tmp = (structinet_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();//对IP层进行初始化协议初始化完成后再执行dev_init()设备的初始化。
这是大体的一个初始化流程,讨论的不是很详细,后续会进行Linux内核网络栈源代码的详细分析。