加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > Linux > 正文

Linux内核--网络栈实现分析(一)--网络栈初始化--转

发布时间:2020-12-13 14:05:34 所属栏目:Linux 来源:网络整理
导读:转载地址? 作者:闫明 ?本文分析基于内核Linux Kernel 1.2.13 以后的系列博文将深入分析Linux内核的网络栈实现原理,这里看到曹桂平博士的分析后,也决定选择Linux内核1.2.13版本进行分析。 原因如下: 1.功能和网络栈层次已经非常清晰 2.该版本与其后续版

转载地址?

作者:闫明

?本文分析基于内核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中

下面是该函数的实现

<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
[cpp]?<a class="ViewSource" title="view plain" href="http://blog.csdn.net/geekcome/article/details/7488828"&gt;view plain<a class="CopyToClipboard" title="copy" href="http://blog.csdn.net/geekcome/article/details/7488828"&gt;copy

?
    ?sock_init()??
  1. {??
  2. ?????i;??
  3. ??
  4. ????printk();??
  5. ??
  6. ?????
  7. ?
  8. ??
  9. ???????
  10. ?????(i?=?0;?i?
  11. ??
  12. ?????
  13. ?
  14. ??
  15. ??
  16. ????proto_init();??
  17. ??
  18. ??
  19. ?????
  20. ?
  21. ??
  22. ??
  23. ????dev_init();??
  24. ????
  25. ?????
  26. ?
  27. ??
  28. ??
  29. ????bh_base[NET_BH].routine=?net_bh;??
  30. ????enable_bh(NET_BH);??
  31. ??
  32. }??

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

<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
[cpp]?<a class="ViewSource" title="view plain" href="http://blog.csdn.net/geekcome/article/details/7488828"&gt;view plain<a class="CopyToClipboard" title="copy" href="http://blog.csdn.net/geekcome/article/details/7488828"&gt;copy

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

可以看到,这里实际上就是一系列操作的函数,有点类似于文件系统中的file_operations。通过参数传递socket完成操作。

接下来是proto_init()协议初始化。

<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
[cpp]?<a class="ViewSource" title="view plain" href="http://blog.csdn.net/geekcome/article/details/7488828"&gt;view plain<a class="CopyToClipboard" title="copy" href="http://blog.csdn.net/geekcome/article/details/7488828"&gt;copy

?
    ?proto_init()??
  1. {??
  2. ??????net_proto?protocols[];??????
  3. ?????net_proto?*pro;??
  4. ??
  5. ??????
  6. ????pro?=?protocols;??
  7. ?????(pro->name?!=?NULL)???
  8. ????{??
  9. ????????(*pro->init_func)(pro);??
  10. ????????pro++;??
  11. ????}??
  12. ??????
  13. }??

全局的protocols定义如下:

<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
[cpp]?<a class="ViewSource" title="view plain" href="http://blog.csdn.net/geekcome/article/details/7488828"&gt;view plain<a class="CopyToClipboard" title="copy" href="http://blog.csdn.net/geekcome/article/details/7488828"&gt;copy

?
    ?net_proto?protocols[]?=?{??
  1. ??
  2. ??{?,?unix_proto_init?},??
  3. ??
  4. ??
  5. ??{?,????p8022_proto_init?},??
  6. ??{?,?snap_proto_init?},??
  7. ??
  8. ??
  9. ??{?,????ax25_proto_init?},??
  10. ??
  11. ??
  12. ??{?,?inet_proto_init?},??
  13. ??
  14. ??
  15. ??{?,??ipx_proto_init?},??
  16. ??
  17. ??
  18. ??{?,??atalk_proto_init?},??
  19. ??
  20. ??{?NULL,???NULL????????}??
  21. };??

而结构体net_proto的定义net.h中为

<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
[cpp]?<a class="ViewSource" title="view plain" href="http://blog.csdn.net/geekcome/article/details/7488828"&gt;view plain<a class="CopyToClipboard" title="copy" href="http://blog.csdn.net/geekcome/article/details/7488828"&gt;copy

?
    ?net_proto?{??
  1. ?????*name;???????
  2. ?????(*init_func)(?net_proto?*);????
  3. };??

以后注重讨论标准的INET域让我们回到proto_init()函数

接下来会执行inet_proto_init()函数,进行INET域协议的初始化。该函数的实现在net/inet/af_inet.c中

其中的

(void) sock_register(inet_proto_ops.family,&inet_proto_ops);

<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
[cpp]?<a class="ViewSource" title="view plain" href="http://blog.csdn.net/geekcome/article/details/7488828"&gt;view plain<a class="CopyToClipboard" title="copy" href="http://blog.csdn.net/geekcome/article/details/7488828"&gt;copy

?
    ?sock_register(?family,??proto_ops?*ops)??
  1. {??
  2. ?????i;??
  3. ??
  4. ????cli();??
  5. ????(i?=?0;?i???
  6. ????{??
  7. ?????????(pops[i]?!=?NULL)???
  8. ????????????;??
  9. ????????pops[i]?=?ops;??
  10. ????????pops[i]->family?=?family;??
  11. ????????sti();??
  12. ????????(i);??
  13. ????}??
  14. ????sti();??
  15. ????(-ENOMEM);??
  16. }??

参数中的inet_proto_ops定义如下:

<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
[cpp]?<a class="ViewSource" title="view plain" href="http://blog.csdn.net/geekcome/article/details/7488828"&gt;view plain<a class="CopyToClipboard" title="copy" href="http://blog.csdn.net/geekcome/article/details/7488828"&gt;copy

?
    ??proto_ops?inet_proto_ops?=?{??
  1. ????AF_INET,??
  2. ??
  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域的操作函数。

然后

[cpp]?
?
    printk();??
  1. (p?=?inet_protocol_base;?p?!=?NULL;)???
  2. {??
  3. ?????inet_protocol?*tmp?=?(?inet_protocol?*)?p->next;??
  4. ????inet_add_protocol(p);??
  5. ????printk(,p->name,tmp?:);??
  6. ????p?=?tmp;??
  7. }??
  8. ?
  9. ?
  10. ??
  11. arp_init();??
  12. ?????
  13. ?
  14. ??
  15. ip_init();??

协议初始化完成后再执行dev_init()设备的初始化。

这是大体的一个初始化流程,讨论的不是很详细,后续会进行Linux内核网络栈源代码的详细分析。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读