转载地址 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内核。原理相同。

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

进行前期的准备工作后,系统跳转到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中

下面是该函数的实现

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

其中的地址族协议初始化语句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中

  1. struct proto_ops {
  2. int   family;
  3. int   (*create)   (struct socket *sock, int protocol);
  4. int   (*dup)      (struct socket *newsock, struct socket *oldsock);
  5. int   (*release)  (struct socket *sock, struct socket *peer);
  6. int   (*bind)     (struct socket *sock, struct sockaddr *umyaddr,
  7. int sockaddr_len);
  8. int   (*connect)  (struct socket *sock, struct sockaddr *uservaddr,
  9. int sockaddr_len, int flags);
  10. int   (*socketpair)   (struct socket *sock1, struct socket *sock2);
  11. int   (*accept)   (struct socket *sock, struct socket *newsock,
  12. int flags);
  13. int   (*getname)  (struct socket *sock, struct sockaddr *uaddr,
  14. int *usockaddr_len, int peer);
  15. int   (*read)     (struct socket *sock, char *ubuf, int size,
  16. int nonblock);
  17. int   (*write)    (struct socket *sock, char *ubuf, int size,
  18. int nonblock);
  19. int   (*select)   (struct socket *sock, int sel_type,
  20. select_table *wait);
  21. int   (*ioctl)    (struct socket *sock, unsigned int cmd,
  22. unsigned long arg);
  23. int   (*listen)   (struct socket *sock, int len);
  24. int   (*send)     (struct socket *sock, void *buff, int len, int nonblock,
  25. unsigned flags);
  26. int   (*recv)     (struct socket *sock, void *buff, int len, int nonblock,
  27. unsigned flags);
  28. int   (*sendto)   (struct socket *sock, void *buff, int len, int nonblock,
  29. unsigned flags, struct sockaddr *, int addr_len);
  30. int   (*recvfrom) (struct socket *sock, void *buff, int len, int nonblock,
  31. unsigned flags, struct sockaddr *, int *addr_len);
  32. int   (*shutdown) (struct socket *sock, int flags);
  33. int   (*setsockopt)   (struct socket *sock, int level, int optname,
  34. char *optval, int optlen);
  35. int   (*getsockopt)   (struct socket *sock, int level, int optname,
  36. char *optval, int *optlen);
  37. int   (*fcntl)    (struct socket *sock, unsigned int cmd,
  38. unsigned long arg);
  39. };

可以看到,这里实际上就是一系列操作的函数,有点类似于文件系统中的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. /* Kick all configured protocols. */
  6. pro = protocols;
  7. while (pro->name != NULL)
  8. {
  9. (*pro->init_func)(pro);
  10. pro++;
  11. }
  12. /* We're all done... */
  13. }

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

参数中的inet_proto_ops定义如下:

  1. static struct proto_ops inet_proto_ops = {
  2. AF_INET,
  3. inet_create,
  4. inet_dup,
  5. inet_release,
  6. inet_bind,
  7. inet_connect,
  8. inet_socketpair,
  9. inet_accept,
  10. inet_getname,
  11. inet_read,
  12. inet_write,
  13. inet_select,
  14. inet_ioctl,
  15. inet_listen,
  16. inet_send,
  17. inet_recv,
  18. inet_sendto,
  19. inet_recvfrom,
  20. inet_shutdown,
  21. inet_setsockopt,
  22. inet_getsockopt,
  23. inet_fcntl,
  24. };

其中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内核网络栈源代码的详细分析。

Linux内核--网络栈实现分析(一)--网络栈初始化--转的更多相关文章

  1. linux内核SPI总线驱动分析(一)(转)

    linux内核SPI总线驱动分析(一)(转) 下面有两个大的模块: 一个是SPI总线驱动的分析            (研究了具体实现的过程) 另一个是SPI总线驱动的编写(不用研究具体的实现过程) ...

  2. Linux内核态抢占机制分析(转)

    Linux内核态抢占机制分析  http://blog.sina.com.cn/s/blog_502c8cc401012pxj.html 摘 要]本文首先介绍非抢占式内核(Non-Preemptive ...

  3. linux内核中链表代码分析---list.h头文件分析(一)【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637596.html linux内核中链表代码分析---list.h头文件分析(一) 16年2月27日17 ...

  4. linux内核中链表代码分析---list.h头文件分析(二)【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637598.html linux内核中链表代码分析---list.h头文件分析(二) 16年2月28日16 ...

  5. Linux内核哈希表分析与应用

        目录(?)[+]   Linux内核哈希表分析与应用 Author:tiger-johnTime:2012-12-20mail:jibo.tiger@gmail.comBlog:http:// ...

  6. Linux内核抢占实现机制分析【转】

    Linux内核抢占实现机制分析 转自:http://blog.chinaunix.net/uid-24227137-id-3050754.html [摘要]本文详解了Linux内核抢占实现机制.首先介 ...

  7. (转)Linux内核基数树应用分析

    Linux内核基数树应用分析 ——lvyilong316 基数树(Radix tree)可看做是以二进制位串为关键字的trie树,是一种多叉树结构,同时又类似多层索引表,每个中间节点包含指向多个节点的 ...

  8. Linux内核中断和异常分析(中)

    在linux内核中,每一个能够发出中断请求的硬件设备控制器都有一条名为IRQ的输出线.所有现在存在的IRQ线都与一个名为可编程中断控制器的硬件电路的输入引脚相连,上次讲到单片机的时候,我就讲到了单片机 ...

  9. 【内核】linux内核启动流程详细分析

    Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件,包括内核入口ENTRY(stext)到start_kernel间的初始化代码, 主要作用 ...

  10. 【内核】linux内核启动流程详细分析【转】

    转自:http://www.cnblogs.com/lcw/p/3337937.html Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件 ...

随机推荐

  1. JDBC中DAO事务函数模版

    DAO事物函数模版1: public void OrderFinsByPage(){ Connection conn = null; PreparedStatement pstmt = null; R ...

  2. cocos2d-x 详解之 CCTexture2D(纹理图片)和 CCTextureCache(纹理缓存)

    精灵和动画都涉及到纹理图片的使用,所以在研究精灵与动画之前,我们先来了解一下纹理图片类CCTexture2D和纹理缓存CCTextureCache的原理: 当一张图片被加载到内存后,它是以纹理的形式存 ...

  3. Blogilo:Ubuntu下“wlw”

    原文首发:http://www.ido321.com/1274.html 这一篇博客是在Ubuntu的博客客户端blogilo下发布的,感觉有点高大上,特此发布一篇博文记录一下.      博客发布在 ...

  4. Multiclass Classification

    之前我们都是在Binary classification的基础上学习算法和知识. 如何使用Binary classification算法进行Multiclass classification呢? (一 ...

  5. iOS Development Learning 13Nov

    关注了关东升老师在博客园的iOS开发博客. 在使用能力课堂观看智捷课堂的iOS8开发视频教程.观看到Part1 课时3 Xcode中的iOS工程模板

  6. mysql 游标最后一行 重复问题

    BEGINdeclare p_id INT;declare p_Code INT default 0;declare p_Logo INT default 0;declare p_Name varch ...

  7. <Chapter 2>2-2-1.用户偏好模式(The User Preferences Pattern)

    在这个模块中我们要创建的应用是一个简单的钟.当一个用户访问这个网站时,这个应用会更具服务器的系统时间显示当前的时间.默认情况下,这个应用使用标准国际时间(UTC)时区显示当前时间.用户可以使用Goog ...

  8. Excel动态生成JSON

    在最近的一个项目中,有大量的数据源来至Excel,转成JSON供前台使用.Excel数据是人工录入的,难免会有错误,所以中间会有逻辑检查.在C#中读取Excel的方式有很多,网上一搜一大堆,这里我也贴 ...

  9. 学习使用Markdown标记语言

    学习如何使用Markdown进行文本编辑 使用教程   大家若是经常逛Github,就知道其中有一个文件叫做README.MD.我一开始也不知道这个.MD是什么意思,后来我自己写了一次,就知道了这一种 ...

  10. 怎样提交FIREDAC数据集的DELTA到中间件然后保存进数据库

    你可以在客户端序列FireDAC数据集的DELTA , 将序列后的STREAM发送给中间件, 中间件的TFDQuery或TFDMemTable调用LOADFROMSTREAM()方法加载流, 然后调用 ...