Linux网络中数据包的接收过程是怎样的

发布时间:2022-01-07 16:31:46 作者:柒染
来源:亿速云 阅读:154

本篇文章为大家展示了Linux网络中数据包的接收过程是怎样的,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

下面将介绍在Linux系统中,数据包是如何一步一步从网卡传到进程手中的。

如果英文没有问题,强烈建议阅读后面参考里的两篇文章,里面介绍的更详细。

小编只讨论以太网的物理网卡,不涉及虚拟设备,并且以一个UDP包的接收过程作为示例.

示例里列出的函数调用关系来自于kernel  3.13.0,如果你的内核不是这个版本,函数名称和相关路径可能不一样,但背后的原理应该是一样的(或者有细微差别)

网卡到内存

网卡需要有驱动才能工作,驱动是加载到内核中的模块,负责衔接网卡和内核的网络模块,驱动在加载的时候将自己注册进网络模块,当相应的网卡收到数据包时,网络模块会调用相应的驱动程序处理数据。

下图展示了数据包(packet)如何进入内存,并被内核的网络模块开始处理:

                   +-----+                    |     |                            Memroy +--------+   1     |     |  2  DMA     +--------+--------+--------+--------+ | Packet |-------->| NIC |------------>| Packet | Packet | Packet | ...... | +--------+         |     |             +--------+--------+--------+--------+                    |     |<--------+                    +-----+         |                       |            +---------------+                       |                            |                     3 | Raise IRQ                  | Disable IRQ                       |                          5 |                       |                            |                       &darr;                            |                    +-----+                   +------------+                    |     |  Run IRQ handler  |            |                    | CPU |------------------>| NIC Driver |                    |     |       4           |            |                    +-----+                   +------------+                                                    |                                                 6  | Raise soft IRQ                                                    |                                                    &darr;

1: 数据包从外面的网络进入物理网卡。如果目的地址不是该网卡,且该网卡没有开启混杂模式,该包会被网卡丢弃。

2: 网卡将数据包通过DMA的方式写入到指定的内存地址,该地址由网卡驱动分配并初始化。注: 老的网卡可能不支持DMA,不过新的网卡一般都支持。

3: 网卡通过硬件中断(IRQ)通知CPU,告诉它有数据来了

4: CPU根据中断表,调用已经注册的中断函数,这个中断函数会调到驱动程序(NIC Driver)中相应的函数

5:  驱动先禁用网卡的中断,表示驱动程序已经知道内存中有数据了,告诉网卡下次再收到数据包直接写内存就可以了,不要再通知CPU了,这样可以提高效率,避免CPU不停的被中断。

6:  启动软中断。这步结束后,硬件中断处理函数就结束返回了。由于硬中断处理程序执行的过程中不能被中断,所以如果它执行时间过长,会导致CPU没法响应其它硬件的中断,于是内核引入软中断,这样可以将硬中断处理函数中耗时的部分移到软中断处理函数里面来慢慢处理。

内核的网络模块

软中断会触发内核网络模块中的软中断处理函数,后续流程如下

                                            +-----+                                     14      |     |                                +----------->| NIC |                                |            |     |                                |Enable IRQ  +-----+                                |                                |                          +------------+                                      Memroy                          |            |        Read           +--------+--------+--------+--------+         +--------------->| NIC Driver |<--------------------- | Packet | Packet | Packet | ...... |         |                |            |          9            +--------+--------+--------+--------+         |                +------------+         |                      |    |        skb    Poll | 8      Raise softIRQ | 6  +-----------------+         |                      |             10       |         |                      &darr;                      &darr; +---------------+  Call  +-----------+        +------------------+ | net_rx_action |<-------| ksoftirqd |        | napi_gro_receive | +---------------+   7    +-----------+        +------------------+                                                       |                                                       | 11                                                       &darr;                                            +--------------------------+    12      +------------------------+                                            | __netif_receive_skb_core |----------->| packet taps(AF_PACKET) |                                            +--------------------------+            +------------------------+                                                       |                                                       | 13                                                       &darr;                                              +-----------------+                                              | protocol layers |                                              +-----------------+

7:  内核中的ksoftirqd进程专门负责软中断的处理,当它收到软中断后,就会调用相应软中断所对应的处理函数,对于上面第6步中是网卡驱动模块抛出的软中断,ksoftirqd会调用网络模块的net_rx_action函数

8: net_rx_action调用网卡驱动里的poll函数来一个一个的处理数据包

9: 在pool函数中,驱动会一个接一个的读取网卡写到内存中的数据包,内存中数据包的格式只有驱动知道

10: 驱动程序将内存中的数据包转换成内核网络模块能识别的skb格式,然后调用napi_gro_receive函数

11:  napi_gro_receive会处理GRO相关的内容,也就是将可以合并的数据包进行合并,这样就只需要调用一次协议栈,接着调用__netif_receive_skb_core

12:  看是不是有AF_PACKET类型的socket(也就是我们常说的原始套接字),如果有的话,拷贝一份数据给它。tcpdump抓包就是抓的这里的包。

13: 调用协议栈相应的函数,将数据包交给协议栈处理。

14: 待内存中的所有数据包被处理完成后(即poll函数执行完成),启用网卡的硬中断,这样下次网卡再收到数据的时候就会通知CPU

协议栈

IP层

由于是UDP包,所以***步会进入IP层,然后一级一级的函数往下调:

|           |           &darr;         promiscuous mode &&       +--------+    PACKET_OTHERHOST (set by driver)   +-----------------+       | ip_rcv |-------------------------------------->| drop this packet|       +--------+                                       +-----------------+           |           |           &darr; +---------------------+ | NF_INET_PRE_ROUTING | +---------------------+           |           |           &darr;       +---------+       |         | enabled ip forword  +------------+        +----------------+       | routing |-------------------->| ip_forward |------->| NF_INET_FOWARD |       |         |                     +------------+        +----------------+       +---------+                                                   |           |                                                         |           | destination IP is local                                 &darr;           &darr;                                                 +---------------+  +------------------+                                       | dst_output_sk |  | ip_local_deliver |                                       +---------------+  +------------------+           |           |           &darr;  +------------------+  | NF_INET_LOCAL_IN |  +------------------+           |           |           &darr;     +-----------+     | UDP layer |     +-----------+

UDP层

 |          |          &darr;      +---------+            +-----------------------+      | udp_rcv |----------->| __udp4_lib_lookup_skb |      +---------+            +-----------------------+          |          |          &darr; +--------------------+      +-----------+ | sock_queue_rcv_skb |----->| sk_filter | +--------------------+      +-----------+          |          |          &darr; +------------------+ | __skb_queue_tail | +------------------+          |          |          &darr;  +---------------+  | sk_data_ready |  +---------------+

调用完sk_data_ready之后,一个数据包处理完成,等待应用层程序来读取,上面所有函数的执行过程都在软中断的上下文中。

socket

应用层一般有两种方式接收数据,一种是recvfrom函数阻塞在那里等着数据来,这种情况下当socket收到通知后,recvfrom就会被唤醒,然后读取接收队列的数据;另一种是通过epoll或者select监听相应的socket,当收到通知后,再调用recvfrom函数去读取接收队列的数据。两种情况都能正常的接收到相应的数据包。

上述内容就是Linux网络中数据包的接收过程是怎样的,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注亿速云行业资讯频道。

推荐阅读:
  1. 路由器转发数据包的封装过程是怎样的
  2. linux Scapy 进行arp数据包详细过程

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

linux

上一篇:Java中的四种引用是什么

下一篇:c++显式栈如何实现递归

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》