Linux内核--网络栈实现分析


本文分析基于内核Linux Kernel 1.2.13

以后的系列博文将深入分析Linux内核的网络栈实现原理,这里看到曹桂平博士的分析后,也决定选择Linux内核1.2.13版本进行分析。

原因如下:

1.功能和网络栈层次已经非常清晰

2.该版本与其后续版本的衔接性较好

3.复杂度相对新的内核版本较小,复杂度低,更容易把握网络内核的实质

4.该内核版本比较系统资料可以查询

下面开始零基础分析Linux内核网络部分的初始化过程。

经过系统加电后执行的bootsect.S,setup.S,head.S,可以参考以前分析的0.11内核。原理相同。

  1. Linux0.11内核--启动引导代码分析bootsect.s 
  2. Linux0.11内核--启动引导代码分析setup.s 
  3. Linux0.11内核--idt(中断描述符表的初始化)head.s分析

进行前期的准备工作后,系统跳转到init/main.c下的start_kernel函数执行。

网络栈的层次结构如下图:(注:该图片摘自《Linux内核网络栈源代码情景分析》)


start_kernel函数经过平台初始化,内存初始化,陷阱初始化,中断初始化,进程调度初始化,缓冲区初始化等,然后执行socket_init(),最后开中断执行init()。

内核的网络战初始化函数socket_init()函数的实现在net/socket.c中

下面是该函数的实现

  1. void sock_init(void)//网络栈初始化   
  2. {  
  3.     int i;  
  4.   
  5.     printk("Swansea University Computer Society NET3.019\n");  
  6.   
  7.     /* 
  8.      *  Initialize all address (protocol) families.  
  9.      */  
  10.        
  11.     for (i = 0; i < NPROTO; ++i) pops[i] = NULL;  
  12.   
  13.     /* 
  14.      *  Initialize the protocols module.  
  15.      */  
  16.   
  17.     proto_init();  
  18.   
  19. #ifdef CONFIG_NET   
  20.     /*  
  21.      *  Initialize the DEV module.  
  22.      */  
  23.   
  24.     dev_init();  
  25.     
  26.     /* 
  27.      *  And the bottom half handler  
  28.      */  
  29.   
  30.     bh_base[NET_BH].routine= net_bh;  
  31.     enable_bh(NET_BH);  
  32. #endif     
  33. }  
其中的地址族协议初始化语句for (i = 0; i < NPROTO; ++i) pops[i] = NULL;

这里文件中定义的NPROTO为16

#define NPROTO 16 /* 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中

  1. struct proto_ops {  
  2.   int   family;  
  3.   
  4.   int   (*create)   (struct socket *sock, int protocol);  
  5.   int   (*dup)      (struct socket *newsock, struct socket *oldsock);  
  6.   int   (*release)  (struct socket *sock, struct socket *peer);  
  7.   int   (*bind)     (struct socket *sock, struct sockaddr *umyaddr,  
  8.              int sockaddr_len);  
  9.   int   (*connect)  (struct socket *sock, struct sockaddr *uservaddr,  
  10.              int sockaddr_len, int flags);  
  11.   int   (*socketpair)   (struct socket *sock1, struct socket *sock2);  
  12.   int   (*accept)   (struct socket *sock, struct socket *newsock,  
  13.              int flags);  
  14.   int   (*getname)  (struct socket *sock, struct sockaddr *uaddr,  
  15.              int *usockaddr_len, int peer);  
  16.   int   (*read)     (struct socket *sock, char *ubuf, int size,  
  17.              int nonblock);  
  18.   int   (*write)    (struct socket *sock, char *ubuf, int size,  
  19.              int nonblock);  
  20.   int   (*select)   (struct socket *sock, int sel_type,  
  21.              select_table *wait);  
  22.   int   (*ioctl)    (struct socket *sock, unsigned int cmd,  
  23.              unsigned long arg);  
  24.   int   (*listen)   (struct socket *sock, int len);  
  25.   int   (*send)     (struct socket *sock, void *buff, int len, int nonblock,  
  26.              unsigned flags);  
  27.   int   (*recv)     (struct socket *sock, void *buff, int len, int nonblock,  
  28.              unsigned flags);  
  29.   int   (*sendto)   (struct socket *sock, void *buff, int len, int nonblock,  
  30.              unsigned flags, struct sockaddr *, int addr_len);  
  31.   int   (*recvfrom) (struct socket *sock, void *buff, int len, int nonblock,  
  32.              unsigned flags, struct sockaddr *, int *addr_len);  
  33.   int   (*shutdown) (struct socket *sock, int flags);  
  34.   int   (*setsockopt)   (struct socket *sock, int level, int optname,  
  35.              char *optval, int optlen);  
  36.   int   (*getsockopt)   (struct socket *sock, int level, int optname,  
  37.              char *optval, int *optlen);  
  38.   int   (*fcntl)    (struct socket *sock, unsigned int cmd,  
  39.              unsigned long arg);      
  40. };  
可以看到,这里实际上就是一系列操作的函数,有点类似于文件系统中的file_operations。通过参数传递socket完成操作。

接下来是proto_init()协议初始化。

  1. void proto_init(void)  
  2. {  
  3.     extern struct net_proto protocols[];    /* Network protocols 全局变量,定义在protocols.c中 */  
  4.     struct net_proto *pro;  
  5.   
  6.     /* Kick all configured protocols. */  
  7.     pro = protocols;  
  8.     while (pro->name != NULL)   
  9.     {  
  10.         (*pro->init_func)(pro);  
  11.         pro++;  
  12.     }  
  13.     /* We're all done... */  
  14. }  
全局的protocols定义如下:
  1. struct net_proto protocols[] = {  
  2. #ifdef  CONFIG_UNIX   
  3.   { "UNIX", unix_proto_init },  
  4. #endif   
  5. #if defined(CONFIG_IPX)||defined(CONFIG_ATALK)     
  6.   { "802.2",    p8022_proto_init },  
  7.   { "SNAP", snap_proto_init },  
  8. #endif   
  9. #ifdef CONFIG_AX25     
  10.   { "AX.25",    ax25_proto_init },  
  11. #endif     
  12. #ifdef  CONFIG_INET   
  13.   { "INET", inet_proto_init },  
  14. #endif   
  15. #ifdef  CONFIG_IPX   
  16.   { "IPX",  ipx_proto_init },  
  17. #endif   
  18. #ifdef CONFIG_ATALK   
  19.   { "DDP",  atalk_proto_init },  
  20. #endif   
  21.   { NULL,   NULL        }  
  22. };  
而结构体net_proto的定义net.h中为
  1. struct net_proto {  
  2.     char *name;     /* Protocol name */  
  3.     void (*init_func)(struct net_proto *);  /* Bootstrap */  
  4. };  
以后注重讨论标准的INET域

让我们回到proto_init()函数

接下来会执行inet_proto_init()函数,进行INET域协议的初始化。该函数的实现在net/inet/af_inet.c中

其中的

(void) sock_register(inet_proto_ops.family, &inet_proto_ops);

  1. int sock_register(int family, struct proto_ops *ops)  
  2. {  
  3.     int i;  
  4.   
  5.     cli();//关中断   
  6.     for(i = 0; i < NPROTO; i++) //查找一个可用的空闲表项   
  7.     {  
  8.         if (pops[i] != NULL)   
  9.             continue;//如果不空,则跳过   
  10.         pops[i] = ops;//进行赋值   
  11.         pops[i]->family = family;  
  12.         sti();//开中断   
  13.         return(i);//返回用于刚刚注册的协议向量号   
  14.     }  
  15.     sti();//出现异常,也要开中断   
  16.     return(-ENOMEM);  
  17. }  

参数中的inet_proto_ops定义如下:

  1. static struct proto_ops inet_proto_ops = {  
  2.     AF_INET,  
  3.   
  4.     inet_create,  
  5.     inet_dup,  
  6.     inet_release,  
  7.     inet_bind,  
  8.     inet_connect,  
  9.     inet_socketpair,  
  10.     inet_accept,  
  11.     inet_getname,   
  12.     inet_read,  
  13.     inet_write,  
  14.     inet_select,  
  15.     inet_ioctl,  
  16.     inet_listen,  
  17.     inet_send,  
  18.     inet_recv,  
  19.     inet_sendto,  
  20.     inet_recvfrom,  
  21.     inet_shutdown,  
  22.     inet_setsockopt,  
  23.     inet_getsockopt,  
  24.     inet_fcntl,  
  25. };  
其中AF_INET宏定义为2,即INET协议族号为2,后面是函数指针,INET域的操作函数。

然后

  1. printk("IP Protocols: ");  
  2. for(p = inet_protocol_base; p != NULL;) //将inet_protocol_base指向的一个inet_protocol结构体加入数组inet_protos中   
  3. {  
  4.     struct inet_protocol *tmp = (struct inet_protocol *) p->next;  
  5.     inet_add_protocol(p);  
  6.     printk("%s%s",p->name,tmp?", ":"\n");  
  7.     p = tmp;  
  8. }  
  9. /* 
  10.  *  Set the ARP module up 
  11.  */  
  12. arp_init();//对地址解析层进行初始化   
  13.     /* 
  14.      *  Set the IP module up 
  15.      */  
  16. ip_init();//对IP层进行初始化  
协议初始化完成后再执行dev_init()设备的初始化。

这是大体的一个初始化流程,讨论的不是很详细,后续会进行Linux内核网络栈源代码的详细分析。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9..
  • 11
  • 下一页
【内容导航】
第1页:网络栈初始化 第2页:数据包的传递过程
第3页:驱动程序层(上) 第4页:网络层之IP协议(上)
第5页:传输层之UDP协议(上) 第6页:应用层获取数据包(上)
第7页:数据包的传递过程(下) 第8页:应用层发送数据(下)
第9页:传输层之UDP协议(下) 第10页:网络层之IP协议(下)
第11页:驱动程序层(下)

相关内容