转载地址 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. oracle检查点checkpoint信息

    1.关于checkpoint的概述 checkpoint是oracle在数据库一致性关闭.实例恢复和oracle基本操作中不可缺少的机制,包含以下相关的含义: A.检查点的位置(checkpoint ...

  2. 【转】JS设计模式开篇

        (原文地址:http://blog.chinaunix.net/uid-26672038-id-3904513.html)     本文主要讲述一下,什么是设计模式(Design patter ...

  3. JavaScript相关图书推荐

    JavaScript语言精粹(修订版) 作      者 Douglas Crockford(道格拉斯·克罗克福德) 著:赵泽欣 等 译 出 版 社 电子工业出版社 出版时间 2012-09-01 版 ...

  4. Ubuntu中使用pyUSB读取鼠标或键盘的数据程序

    参考 :http://www.orangecoat.com/how-to/read-and-decode-data-from-your-mouse-using-this-pyusb-hack 要注意的 ...

  5. HDU5479 Colmerauer 单调栈+暴力优化

    http://acm.hdu.edu.cn/showproblem.php?pid=5749 思路: bestcoder 84 贡献:所有可能的子矩阵的面积和 //len1:子矩阵所有长的和 ;i&l ...

  6. FS,FT,DFS,DTFT,DFT,FFT的联系和区别

    DCT变换的原理及算法 文库介绍 对于初学数字信号处理(DSP)的人来说,这几种变换是最为头疼的,它们是数字信号处理的理论基础,贯穿整个信号的处理. 学习过<高等数学>和<信号与系统 ...

  7. 配置Tomcat以指定的身份(非root)运行

    本文依赖的环境: CentOS(大部分内容适用于其他Linux发行版) 已安装并配置好JVM环境 已安装并配置好gcc.make等编译工具 1. 下载Tomcat安装包并解压缩 cd /optwget ...

  8. Ubuntu 安装 Sun JDK

    1. 下载    Oracle网站下载JDK7     http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1 ...

  9. Red5下的room

    http://blog.csdn.net/whycold/article/details/6142475 package com.test; import java.util.ArrayList;im ...

  10. [转载]Linux LVM硬盘管理及LVM扩容

    最近项目中一直在用Linux,其中涉及到了Linux的LVM,本来想自己写一篇关于LVM的文章,搜了一下,发现了一篇更好的,转载过来,也感谢作者gaojun 原文Linux LVM硬盘管理及LVM扩容 ...