转载地址 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. linux中配置桥接网络,让虚拟机能够上网

    使用桥接模式最主要的目的就是让虚拟机也能上网,从而有了这篇文章. 1.设置虚拟机的网络连接方式 在设置虚拟机网线的连接方式的时候,注意第一个选择桥接模式,第二个界面名称必须使用和宿主机相同的网卡,然后 ...

  2. CSS快速制作图片轮播的焦点

    来源:http://www.ido321.com/858.html 效果图: 演示地址:http://jsfiddle.net/Web_Code/q5qfd8aL/embedded/result/ 代 ...

  3. htmlcss笔记--标签默认值样式重置css reset(2)

    1.内联,内嵌,行内元素: 默认可以继续跟同类型标签显示: 由内容撑开宽度,内容多宽,会有多宽: span,内嵌标签部支持宽高,即使加了宽高也不支持, 也不支持上下内外边距,但是支持左右内外边距:内上 ...

  4. [Hive - Tutorial] Type System 数据类型

    数据类型Type System Hive supports primitive and complex data types, as described below. See Hive Data Ty ...

  5. 转载-MySQL 加锁处理分析

    MySQL 加锁处理分析 发表于 2013 年 12 月 13 日 由 hedengcheng 1    背景    1 1.1    MVCC:Snapshot Read vs Current Re ...

  6. mysql performance_schema 初探

    mysql  performance_schema 初探: mysql 5.5 版本 新增了一个性能优化的引擎: PERFORMANCE_SCHEMA 这个功能默认是关闭的: 需要设置参数: perf ...

  7. 窥探JVM内存分配和回收的过程

    一.环境 JDK 垃圾收集器 是否启用TLAB 通用JVM参数(堆内存分配见下图) 1.6.0_65 Serial + Serial Old 否 -Xms20m -Xmx20m -Xmn10m -XX ...

  8. 第三百天了 how can I 坚持

    郭娜还是不搭理我,或许是真没有遇到合适的.? 该咋办,好焦虑. 其实态度应该放低点,就这样是找不到对象的. 有必要这么死耗嘛,人生总是这么纠结. 周六年后,周日来吃火锅,这一年又过去了. 睡觉.

  9. java多线程中的volatile和synchronized

    package com.chzhao; public class Volatiletest extends Thread { private static int count = 0; public ...

  10. jquery.loadmask.js

    Quick Start 下载之后的目录结构如下图所示: 使用此插件非常简单,如下步骤所示: 1.  引用jquery,1.2.3以上版本 <script type="text/java ...