转载地址 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. SQL你必须知道的-函数及类型转换

    use MySchoolTwo    --ISNULL(expression,value) :如果 expression不为空则返回 expression ,否则返回 value.    select ...

  2. My implementation of AVL tree

    C++实现的avl平衡树 #include <stdlib.h> #include <time.h> #include <string.h> #include &l ...

  3. [HIve - LanguageManual] Transform [没懂]

    Transform/Map-Reduce Syntax SQL Standard Based Authorization Disallows TRANSFORM TRANSFORM Examples ...

  4. <Chapter 2>2-2-2.开发Java应用(Developing a Java App)

    App Engine的Java网络应用使用了Java Servlet标准接口来和应用服务器交互.一个应用由一个或多个servlet类组成,每个都扩展了(extend)servlet基类.使用一个叫做部 ...

  5. Spring @PostConstruct and @PreDestroy example

    In Spring, you can either implements InitializingBean and DisposableBean interface or specify the in ...

  6. 推荐十款非常优秀的 HTML5 在线设计工具

    网络有很多优秀的设计和开发工具可能大家都不知道,因此这篇文章就向设计师推荐十款优秀 HTML5 在线工具,这些工具能够帮助设计师们设计出更有创意的作品.随着 HTML5 技术的不断成熟,网络上涌现出越 ...

  7. Java计算日期和时间差

    这篇文章将使用两个例子计算两个日期的时间差.1.使用Java SDK.2.使用Joda库.1.使用Java SDK 计算两个Date之间的时间差,基本思路为把Date转换为ms(微秒),然后计算两个微 ...

  8. HTML5画布Canvas

    一.Canvas概念介绍 1.概念 Canvas : 画布 2.作用 : HTML5 Canvas 元素用于图形的绘制, 通过脚本(通常是JavaScript)来完成.它本身只是个图形容器,必须使用脚 ...

  9. 【SQL】导出表数据到Excel中

    打开数据库之后点击新建表查询: ------------------------------------------------------------------------------------ ...

  10. ssl配置

    Apache SSL配置 作者: JeremyWei | 可以转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明网址: http://weizhifeng.net/apache-ssl.h ...