Linux 内核是如何收包

53 阅读5分钟

内核是如何接收到网络包的.

分析流程:

  • 硬中断、软中断.

    • 系统如何区分硬中断是什么类型?中断是由硬件发送给CPU内核来进行处理消息的,而每个硬件都会被分配一个特定的IRQ中断号,而通过这个中断号就可以判断出当前中断是什么类型的中断了.
  • ksoftirqd 线程.

  • 网络模块初始化和网卡初始化.

  • 多队列网卡.

一、网络模块初始化.

操作系统初始化时会对各个字模块进行初始化,这个时候就会对网卡驱动进行初始化,我理解初始化的目的为以下几种:

  1. 设置网卡回调函数

    1. 为每一个CPU都申请一个softnet_data数据结构用来存放各个回调函数,其中就包含硬中断和软中断处理函数.
    2. ethtool 回调函数指针设置.
    3. 设置网卡处于打开状态:开始接受数据包.

二、网卡处理数据包.

当网卡上有数据包到来时,会通过DMA: 不经过CPU 直接进行内存拷贝的方式直接将数据包拷贝到预先分配好的RingBuffer缓存队里中,然后等待CPU来进行处理,那么CPU是什么时候来处理的?是for循环还是有别的通知来进行处理呢?

当网卡将数据DMARingBuffer后,会向CPU发起一个硬中断(网卡会向CPU的相关引脚发送一个电压变化)来表示当前网络有包到达,当CPU收到硬中断后并且判断是网络硬中断则会触发网络模块初始化时注册的硬中断回调函数,而CPU这个时候并不会全部去处理这个数据,而是发送一个软中断告知内核线程ksoftirqd线程,让其去处理网络包. 这个时候网络包就已经开始由内核来处理了。下面是相关流程分析:

image-20230225180844562

ksoftirqd线程会循环监听软中断(软中断就是修改指定内存变量,而ksoftirqd线程就是监听变量是否有被更改而得出是否有软中断发生),这个时候内核线程则会去RingBuffer队列上取出一个包保存为一个skb,并且重新给RingBuffer赋值一个新的skb内存,软中断将包取出之后,交给协议栈进行处理。由于发生硬终端和触发软中断是在一个CPU核上处理的,所以一个包一定是被同一个CPU处理的。

一个简单的流程图:

image-20230225174409956

image-20230225174409956

三、RingBuffer相关.

每一个RingBuffer都会分配一个特定的网络中断号,用于进行中断时告知CPU当前是那个中断类型.

RingBuffer其实就是操作系统在进行初始化时预先分配好内存地址的一个环形队列,具体大小可以通过ethtool -g eth1命令查看.

Ring parameters for eth1:
Pre-set maximums:  # 启动时设置的RingBuffer队列大小. 
RX:             1024 # RX QUEUE初始化长度. 
RX Mini:        0
RX Jumbo:       0
TX:             1024 # TX QUEUE初始化长度. 
Current hardware settings: # 当前配置. 
RX:             1024
RX Mini:        0
RX Jumbo:       0
TX:             1024

而通过命令ethtool -G eth1 RX 4096 TX 4096 可以修改当前RingBuffer的大小. 这种情况就是去应对内核收包的速率小于网卡收包的速率,提高RX队列大小而避免丢包,这种虽然可以避免丢包,但是这种情况可能导致收包延时,当队列变大之后,等待处理的时间则可能会变长,所以如果出现丢包的情况,可以通过调大RingBuffer来避免,但是真正的处理逻辑应该是加速网络包的消费能力。

**问题:**如果发现某个CPU的软中断都集中在一个CPU核上,那么应该怎么处理?

网络软中断都集中咋一个CPU核上,那就说明网卡在处理收包时发送硬中断时只发送给了一个特定的CPU,导致其他CPU没有办法去收包,而我们知道硬中断发生在那个CPU上则软中断也会在该CPU上。所以我们只需要调整硬中断CPU的亲和性即可,将硬中断打散到不同的CPU上去.

四、多队列网卡.

在没有多队列网卡出现之前,网络模块是怎么收发包的:

在多队列网卡还没有出现之前,一个网卡只能申请一个中断号,所以就导致只能有一个队列来进行收包,而一个队列必须绑定一个中断号,所以说只能有一个队列来收包。所以会出现一种情况就是:所有CPU中,总会有一个CPU占用高的情况.

什么是多队列网卡:

允许一个网卡硬件申请多个中断号,并且允许同时初始化多个收包队列,并且可以通过ethtool命令设置每个队列对特定CPU的亲和性,从而将收包打撒到不同的CPU中。

参考文章:

  • 接收包处理流程.
http://arthurchiao.art/blog/linux-net-stack-implementation-rx-zh/
  • 多队列网卡
http://xusenqi.site/2018/11/23/%E7%BD%91%E5%8D%A1%E5%A4%9A%E9%98%9F%E5%88%97%E6%80%BB%E7%BB%93/

网络相关模块开始从头开始梳理,发现自己好菜 哈哈哈哈, 学的时候马马虎虎,学完之后啥也想不起来 哈哈哈。

如果文章有误,请大佬帮忙指出 我即使修正哈