本文分析基於內核Linux Kernel 1.2.13
以後的系列博文將深入分析Linux內核的網絡棧實現原理,這裡看到曹桂平博士的分析後,也決定選擇Linux內核1.2.13版本進行分析。
原因如下:
1.功能和網絡棧層次已經非常清晰
2.該版本與其後續版本的銜接性較好
3.復雜度相對新的內核版本較小,復雜度低,更容易把握網絡內核的實質
4.該內核版本比較系統資料可以查詢
下面開始零基礎分析Linux內核網絡部分的初始化過程。
經過系統加電後執行的bootsect.S,setup.S,head.S,可以參考以前分析的0.11內核。原理相同。
- Linux0.11內核--啟動引導代碼分析bootsect.s http://www.linuxidc.com/Linux/2012-04/59202.htm
- Linux0.11內核--啟動引導代碼分析setup.s http://www.linuxidc.com/Linux/2012-04/59203.htm
- Linux0.11內核--idt(中斷描述符表的初始化)head.s分析 http://www.linuxidc.com/Linux/2012-04/59204.htm
進行前期的准備工作後,系統跳轉到init/main.c下的start_kernel函數執行。
網絡棧的層次結構如下圖:(注:該圖片摘自《Linux內核網絡棧源代碼情景分析》)
start_kernel函數經過平台初始化,內存初始化,陷阱初始化,中斷初始化,進程調度初始化,緩沖區初始化等,然後執行socket_init(),最後開中斷執行init()。
內核的網絡戰初始化函數socket_init()函數的實現在net/socket.c中
下面是該函數的實現
- void sock_init(void)//網絡棧初始化
- {
- int i;
-
- printk("Swansea University Computer Society NET3.019\n");
-
- /*
- * Initialize all address (protocol) families.
- */
-
- for (i = 0; i < NPROTO; ++i) pops[i] = NULL;
-
- /*
- * 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 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中
- struct proto_ops {
- int family;
-
- int (*create) (struct socket *sock, int protocol);
- int (*dup) (struct socket *newsock, struct socket *oldsock);
- int (*release) (struct socket *sock, struct socket *peer);
- int (*bind) (struct socket *sock, struct sockaddr *umyaddr,
- int sockaddr_len);
- int (*connect) (struct socket *sock, struct sockaddr *uservaddr,
- int sockaddr_len, int flags);
- int (*socketpair) (struct socket *sock1, struct socket *sock2);
- int (*accept) (struct socket *sock, struct socket *newsock,
- int flags);
- int (*getname) (struct socket *sock, struct sockaddr *uaddr,
- int *usockaddr_len, int peer);
- int (*read) (struct socket *sock, char *ubuf, int size,
- int nonblock);
- int (*write) (struct socket *sock, char *ubuf, int size,
- int nonblock);
- int (*select) (struct socket *sock, int sel_type,
- select_table *wait);
- int (*ioctl) (struct socket *sock, unsigned int cmd,
- unsigned long arg);
- int (*listen) (struct socket *sock, int len);
- int (*send) (struct socket *sock, void *buff, int len, int nonblock,
- unsigned flags);
- int (*recv) (struct socket *sock, void *buff, int len, int nonblock,
- unsigned flags);
- int (*sendto) (struct socket *sock, void *buff, int len, int nonblock,
- unsigned flags, struct sockaddr *, int addr_len);
- int (*recvfrom) (struct socket *sock, void *buff, int len, int nonblock,
- unsigned flags, struct sockaddr *, int *addr_len);
- int (*shutdown) (struct socket *sock, int flags);
- int (*setsockopt) (struct socket *sock, int level, int optname,
- char *optval, int optlen);
- int (*getsockopt) (struct socket *sock, int level, int optname,
- char *optval, int *optlen);
- int (*fcntl) (struct socket *sock, unsigned int cmd,
- unsigned long arg);
- };
可以看到,這裡實際上就是一系列操作的函數,有點類似於文件系統中的file_operations。通過參數傳遞socket完成操作。
接下來是proto_init()協議初始化。
- void proto_init(void)
- {
- extern struct net_proto protocols[]; /* Network protocols 全局變量,定義在protocols.c中 */
- struct net_proto *pro;
-
- /* Kick all configured protocols. */
- pro = protocols;
- while (pro->name != NULL)
- {
- (*pro->init_func)(pro);
- pro++;
- }
- /* We're all done... */
- }
全局的protocols定義如下:
- struct net_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中為
- struct net_proto {
- char *name; /* Protocol name */
- void (*init_func)(struct net_proto *); /* Bootstrap */
- };
以後注重討論標准的INET域
讓我們回到proto_init()函數
接下來會執行inet_proto_init()函數,進行INET域協議的初始化。該函數的實現在net/inet/af_inet.c中
其中的
(void) sock_register(inet_proto_ops.family, &inet_proto_ops);
- int sock_register(int family, struct proto_ops *ops)
- {
- int i;
-
- cli();//關中斷
- for(i = 0; i < NPROTO; i++) //查找一個可用的空閒表項
- {
- if (pops[i] != NULL)
- continue;//如果不空,則跳過
- pops[i] = ops;//進行賦值
- pops[i]->family = family;
- sti();//開中斷
- return(i);//返回用於剛剛注冊的協議向量號
- }
- sti();//出現異常,也要開中斷
- return(-ENOMEM);
- }
參數中的inet_proto_ops定義如下:
- 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,
- };
其中AF_INET宏定義為2,即INET協議族號為2,後面是函數指針,INET域的操作函數。
然後
- printk("IP Protocols: ");
- for(p = inet_protocol_base; p != NULL;) //將inet_protocol_base指向的一個inet_protocol結構體加入數組inet_protos中
- {
- 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();//對IP層進行初始化
協議初始化完成後再執行dev_init()設備的初始化。
這是大體的一個初始化流程,討論的不是很詳細,後續會進行Linux內核網絡棧源代碼的詳細分析。