Linux内核--网络栈实现分析(一)--网络栈初始化--转
转载地址 http://blog.csdn.net/yming0221/article/details/7488828
作者:闫明
本文分析基于内核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内核网络栈源代码情景分析》)

物理层主要提供各种连接的物理设备,如各种网卡,串口卡等;
链路层主要指的是提供对物理层进行访问的各种接口卡的驱动程序,如网卡驱动等;
网路层的作用是负责将网络数据包传输到正确的位置,最重要的网络层协议当然就是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
- }
其中的地址族协议初始化语句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中
- 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内核网络栈源代码的详细分析。
Linux内核--网络栈实现分析(一)--网络栈初始化--转的更多相关文章
- linux内核SPI总线驱动分析(一)(转)
linux内核SPI总线驱动分析(一)(转) 下面有两个大的模块: 一个是SPI总线驱动的分析 (研究了具体实现的过程) 另一个是SPI总线驱动的编写(不用研究具体的实现过程) ...
- Linux内核态抢占机制分析(转)
Linux内核态抢占机制分析 http://blog.sina.com.cn/s/blog_502c8cc401012pxj.html 摘 要]本文首先介绍非抢占式内核(Non-Preemptive ...
- linux内核中链表代码分析---list.h头文件分析(一)【转】
转自:http://blog.chinaunix.net/uid-30254565-id-5637596.html linux内核中链表代码分析---list.h头文件分析(一) 16年2月27日17 ...
- linux内核中链表代码分析---list.h头文件分析(二)【转】
转自:http://blog.chinaunix.net/uid-30254565-id-5637598.html linux内核中链表代码分析---list.h头文件分析(二) 16年2月28日16 ...
- Linux内核哈希表分析与应用
目录(?)[+] Linux内核哈希表分析与应用 Author:tiger-johnTime:2012-12-20mail:jibo.tiger@gmail.comBlog:http:// ...
- Linux内核抢占实现机制分析【转】
Linux内核抢占实现机制分析 转自:http://blog.chinaunix.net/uid-24227137-id-3050754.html [摘要]本文详解了Linux内核抢占实现机制.首先介 ...
- (转)Linux内核基数树应用分析
Linux内核基数树应用分析 ——lvyilong316 基数树(Radix tree)可看做是以二进制位串为关键字的trie树,是一种多叉树结构,同时又类似多层索引表,每个中间节点包含指向多个节点的 ...
- Linux内核中断和异常分析(中)
在linux内核中,每一个能够发出中断请求的硬件设备控制器都有一条名为IRQ的输出线.所有现在存在的IRQ线都与一个名为可编程中断控制器的硬件电路的输入引脚相连,上次讲到单片机的时候,我就讲到了单片机 ...
- 【内核】linux内核启动流程详细分析
Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件,包括内核入口ENTRY(stext)到start_kernel间的初始化代码, 主要作用 ...
- 【内核】linux内核启动流程详细分析【转】
转自:http://www.cnblogs.com/lcw/p/3337937.html Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件 ...
随机推荐
- (三)相遇射线的3D碰撞盒
序 在2D游戏中,我们知道处理碰撞时,需要设置精灵遮罩图.同样,进入3D,处理碰撞时需要3D模型作为“遮罩图”. 索尼克 飞檐走壁 目的 (1)处理模型间的碰撞问题 (2)获取鼠标 ...
- MFC图形图像
一.CDC类 CDC类简介 CDC类是一个设备上下文类. CDC类提供了用来处理显示器或打印机等设备上下文的成员函数,还有处理与窗口客户区关联的显示上下文的成员函数.使用CDC的成员函数可以进行所有的 ...
- Java相关书籍推荐
Java从入门到精通(第3版 附光盘) 作 者 明日科技 编 出 版 社 清华大学出版社 出版时间 2012-08-01 版 次 3 页 数 564 印刷时间 2012- ...
- (转载)OC学习篇之---类的三大特性:封装,继承,多态
之前的一片文章介绍了OC中类的初始化方法和点语法的使用,今天来继续学习OC中的类的三大特性,我们在学习Java的时候都知道,类有三大特性:继承,封装,多态,这个也是介绍类的时候,必须提到的话题,那么今 ...
- EmEditor正则表达式例子
正则表达式中 单词指的是由字母.数字.下划线组合而成的字符串,用符号表示为\w(小写). 空白符包括单字节空格.双字节空格.制表符,用符号表示为\s(小写). 1.匹配被双引号包含的所有字符串(str ...
- android sdk manager 闪退 打不开问题
android sdk manager 闪退 打不开问题 环境 win8系统 如果访问不了 dl-ssl.google.com 网址,在C:\Windows\System32\Drivers\etc ...
- 防asp木马运行
首 页 > 技术支持 防asp木马运行 目前比较流行的ASP木马主要通过三种技术来进行对服务器的相关操作. 一.使用FileSystemObject组件 FileSystemObject可以对文 ...
- Nginx的session一致性问题
session一致性memcached缓存数据库解决方案 1.安装memcached内存数据库 yum –y install memcached 可以用telnet localhost 11211 S ...
- [iOS微博项目 - 1.8] - 各种尺寸图片加载 & 控件不显示研究
A. 图片的加载: [UIImage imageNamed:@"home"]; 加载png图片 一.非retina屏幕 1.3.5 inch(320 x 480) * ...
- 开发WebForm时遇到的问题
在做一个小项目时,一个很长的页面,页面底部有一个contact us form 整个页面我没有使用MVC,而是使用ASP.NET WebForm(.aspx)来实现,实现功能后发现,当用户在页面底部输 ...