本文分析基于内核Linux Kernel 1.2.13

原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7488828

更多请看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html

作者:闫明

以后的系列博文将深入分析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 NPROTO 16 /* 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. javascript笔记:流程控制语句

    一.条件语句 1.if语句 if 语句即条件判断语句,一共有三种格式: (1)if (条件表达式) 语句: var box = 100; if (box >50) { alert('box大于5 ...

  2. Ubuntu 12.10使用apt安装Oracle/Sun JDK

    apt-get install python-software-properties sudo add-apt-repository ppa:webupd8team/java sudo apt-get ...

  3. java中常用数据类型转换器

    /** * 把String转换成long * * @param src 要转换的String * @param def 转换失败时返回此值 * @return 转换好的long */ public s ...

  4. Linux系统man查询命令等级及意义

    1:用户命令,可由任何人启动的 2:系统调用,由内核提供的函数 3:库函数 4:设备,/dev目录下的特殊文件 5:文件格式描述,例如/etc/passwd 6:游戏 7:杂项,例如宏命令包.惯例等 ...

  5. python学习笔记:文件操作和集合(转)

    转自:http://www.nnzhp.cn/article/16/ 这篇博客来说一下python对文件的操作. 对文件的操作分三步: 1.打开文件获取文件的句柄,句柄就理解为这个文件 2.通过文件句 ...

  6. Brocade,Cisco SAN Switch命令对比

  7. Java项目导出war包 security alert:integrity check error”

    操作方法: 首先下载这个jar包 http://pan.baidu.com/s/1sk5uDzf 1.需要先把common/pluns 下的com.genuitec.eclipse.export.wi ...

  8. linux C学习笔记02--共享内存(进程同步)

    system V下3中进程同步:共享内存(shared memory),信号量(semaphore)和消息队列(message queue) 调试了下午,终于调通啦! 运行./c.out 输出共享内存 ...

  9. 【软件工程】用map 实现把英语文本文件词和个数打印出来

    #include <iostream> #include <fstream> #include <string> #include <map> usin ...

  10. jqGrid学习笔记(一)

    3.2.body中的代码 <!-- jqGrid table list4 --> <table id="list4"></table> < ...