本文分析基於內核Linux Kernel 1.2.13
原創作品,轉載請標明http://blog.csdn.net/yming0221/article/details/7488828
更多請看專欄,地址http://blog.csdn.net/column/details/linux-kernel-net.html
作者:閆明
以後的系列博文將深入分析Linux內核的網絡棧實現原理,這裏看到曹桂平博士的分析後,也決定選擇Linux內核1.2.13版本進行分析。
原因如下:
1.功能和網絡棧層次已經非常清晰
2.該版本與其後續版本的銜接性較好
3.複雜度相對新的內核版本較小,複雜度低,更容易把握網絡內核的實質
4.該內核版本比較系統資料可以查詢
下面開始零基礎分析Linux內核網絡部分的初始化過程。
經過系統加電後執行的bootsect.S,setup.S,head.S,可以參考以前分析的0.11內核。原理相同。
進行前期的準備工作後,系統跳轉到init/main.c下的start_kernel函數執行。
網絡棧的層次結構如下圖:(注:該圖片摘自《Linux內核網絡棧源代碼情景分析》)
物理層主要提供各種連接的物理設備,如各種網卡,串口卡等;
鏈路層主要指的是提供對物理層進行訪問的各種接口卡的驅動程序,如網卡驅動等;
網路層的作用是負責將網絡數據包傳輸到正確的位置,最重要的網絡層協議當然就是IP協議了,其實網絡層還有其他的協議如ICMP,ARP,RARP等,只不過不像IP那樣被多數人所熟悉;
傳輸層的作用主要是提供端到端,說白一點就是提供應用程序之間的通信,傳輸層最着名的協議非TCP與UDP協議末屬了;
應用層,顧名思義,當然就是由應用程序提供的,用來對傳輸數據進行語義解釋的“人機界面”層了,比如HTTP,SMTP,FTP等等,其實應用層還不是人們最終所看到的那一層,最上面的一層應該是“解釋層”,負責將數據以各種不同的表項形式最終呈獻到人們眼前。
Linux網絡協議棧結構
1,系統調用接口層,實質是一個面向用戶空間應用程序的接口調用庫,向用戶空間應用程序提供使用網絡服務的接口。
2,協議無關的接口層,就是SOCKET層,這一層的目的是屏蔽底層的不同協議(更準確的來說主要是TCP與UDP,當然還包括RAW IP, SCTP等),以便與系統調用層之間的接口可以簡單,統一。簡單的說,不管我們應用層使用什麼協議,都要通過系統調用接口來建立一個SOCKET,這個SOCKET其實是一個巨大的sock結構,它和下面一層的網絡協議層聯繫起來,屏蔽了不同的網絡協議的不同,只吧數據部分呈獻給應用層(通過系統調用接口來呈獻)。
3,網絡協議實現層,毫無疑問,這是整個協議棧的核心。這一層主要實現各種網絡協議,最主要的當然是IP,ICMP,ARP,RARP,TCP,UDP等。這一層包含了很多設計的技巧與算法,相當的不錯。
4,與具體設備無關的驅動接口層,這一層的目的主要是爲了統一不同的接口卡的驅動程序與網絡協議層的接口,它將各種不同的驅動程序的功能統一抽象爲幾個特殊的動作,如open,close,init等,這一層可以屏蔽底層不同的驅動程序。
5,驅動程序層,這一層的目的就很簡單了,就是建立與硬件的接口層。
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
- }
這裏文件中定義的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... */
- }
- 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 */
- };
讓我們回到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層進行初始化
這是大體的一個初始化流程,討論的不是很詳細,後續會進行Linux內核網絡棧源代碼的詳細分析。