监控和调整Linux网络堆栈的图解指南:接收数据
该博客文章是在我们之前的博客文章《监视和调整Linux网络堆栈:接收数据》的基础上扩展的,该图表带有一系列图表,旨在帮助读者更清晰地了解Linux网络堆栈的工作方式。
监视或调整Linux网络堆栈没有捷径可走。如果有希望对其进行调整或优化,操作员必须努力充分理解各种系统及其相互作用。也就是说,前一篇博客文章的篇幅可能使读者难以概念化各种系统之间的交互方式。希望这篇博客文章能帮助您解决这一问题。
入门
这些图旨在概述Linux网络堆栈的工作方式。因此,许多细节被排除在外。为了获得完整的图片,鼓励读者阅读我们的博客文章,其中详细介绍了网络堆栈的各个方面:监视和调整Linux网络堆栈:接收数据。这些附图的目的是帮助读者形成一个心理模型,以了解内核中的某些系统如何在高层进行交互。
让我们先来看一些重要的初始设置,这对于理解数据包处理是必需的。
初始设置
设备具有多种方式来提醒计算机系统的其余部分某些工作已准备就绪,可以进行处理。对于网络设备,NIC通常会提高IRQ来发出信号,表示已到达数据包并已准备好进行处理。当Linux内核执行IRQ处理程序时,它以非常高的优先级运行,并经常阻止生成其他IRQ。因此,设备驱动程序中的IRQ处理程序必须尽快执行,并将所有长时间运行的工作推迟到此上下文之外执行。这就是为什么存在“ softIRQ”系统的原因。
Linux内核中的“ softIRQ”系统是内核用来处理设备驱动程序IRQ上下文之外的工作的系统。对于网络设备,softIRQ系统负责处理传入的数据包。softIRQ系统在内核引导过程中提前初始化。
上图对应于我们网络博客文章的softIRQ部分,并显示了softIRQ系统及其每CPU内核线程的初始化。
softIRQ系统的初始化如下:
- 软中断内核线程被创建(每个CPU的一个)中
spawn_ksoftirqd
的内核/ softirq.c中与一个呼叫smpboot_register_percpu_thread
从内核/ smpboot.c。从代码中可以看出,该函数run_ksoftirqd
列为thread_fn
,这是将在循环中执行的函数。 - ksoftirqd线程开始在
run_ksoftirqd
函数中执行其处理循环。 - 接下来,
softnet_data
创建结构,每个CPU一个。这些结构引用了用于处理网络数据的重要数据结构。我们将再次看到的是poll_list
。的poll_list
就是NAPI调查工人结构将通过向呼叫添加napi_schedule
或设备驱动程序等NAPI的API。 -
net_dev_init
然后NET_RX_SOFTIRQ
通过调用将softirq 注册到softirq系统open_softirq
,如此处所示。注册的处理程序函数称为net_rx_action
。这是softirq内核线程将执行以处理数据包的功能。
图上的步骤5-8与要处理的数据的到达有关,将在下一节中提到。阅读更多!
数据到达
当网络数据到达NIC时,NIC将使用DMA将数据包数据写入RAM。对于igb
网络驱动程序,在RAM中设置一个环形缓冲区,指向接收到的数据包。重要的是要注意,某些NIC是“多队列” NIC,这意味着它们可以将传入的数据包DMA到RAM中的许多环形缓冲区之一。正如我们将很快看到的那样,此类NIC能够利用多个处理器来处理传入的网络数据。了解有关多队列NIC的更多信息。为了简化起见,上图仅显示了一个环形缓冲区,但是根据您使用的NIC和硬件设置,您的系统上可能会有多个队列。
在网络博客文章的此部分中阅读有关下面描述的过程的更多详细信息。
让我们逐步了解接收数据的过程:
- NIC从网络接收数据。
- NIC使用DMA将网络数据写入RAM。
- NIC发出IRQ。
- 执行设备驱动程序的注册IRQ处理程序。
- IRQ在NIC上被清除,因此它可以为新的数据包到达生成IRQ。
- NAPI softIRQ轮询循环从对的调用开始
napi_schedule
。
调用将napi_schedule
触发上图中的步骤5-8的开始。就像我们将看到的那样,NAPI softIRQ轮询循环是通过简单地翻转位域中的一个位并将一个结构添加到poll_list
处理中而开始的。没有其他工作可以完成,napi_schedule
而这正是驱动程序将处理推迟到softIRQ系统的方式。
继续使用上一节中的数字继续上一节中的图:
-
napi_schedule
在驱动程序中对的调用将驱动程序的NAPI轮询结构添加到poll_list
当前CPU的。 - 设置softirq挂起位,以便
ksoftirqd
此CPU上的进程知道有要处理的数据包。 -
run_ksoftirqd
函数(由ksoftirq
内核线程循环运行)执行。 -
__do_softirq
调用,检查未决的位字段,查看softIRQ是否未决,并调用为未决的softIRQ注册的处理程序:net_rx_action
这将对传入的网络数据进行所有繁重的处理。
重要的是要注意,softIRQ内核线程正在执行net_rx_action
,而不是设备驱动程序IRQ处理程序。
网络数据处理开始
现在,数据处理开始。该net_rx_action
函数(从ksoftirqd
内核线程调用)将开始处理已添加到poll_list
当前CPU的所有NAPI轮询结构。在两种一般情况下添加了轮询结构:
- 从设备驱动程序调用
napi_schedule
。 - 在接收数据包控制的情况下使用处理器间中断。阅读有关接收数据包导向如何使用IPI处理数据包的更多信息。
我们将首先介绍从中获取驱动程序的NAPI结构时发生的情况poll_list
。(下一节将介绍如何为RPS向IPI注册的NAPI结构)。
上面的图在此处进行了深入说明),但可以总结如下:
-
net_rx_action
循环通过检查NAPI轮询列表中的NAPI结构开始。 - 的
budget
和经过时间进行检查,以确保软中断不会独占CPU时间。 - 注册的
poll
函数称为。在这种情况下,该功能igb_poll
由igb
驱动程序注册。 - 驱动程序的
poll
功能从RAM中的环形缓冲区中获取数据包。 - 数据包被移交给
napi_gro_receive
,这将处理可能的通用接收卸载。 - 数据包要么保留用于GRO,调用链就在此处结束,要么将数据包传递到
net_receive_skb
协议栈。
接下来,我们将看到如何net_receive_skb
处理接收数据包控制以在多个CPU之间分配数据包处理负载。
网络数据处理继续
【jpg】
网络数据处理从继续netif_receive_skb
,但是数据的路径取决于是否启用了接收数据包导向(RPS)。默认情况下,“即用型” Linux内核不会启用RPS,并且如果要使用它,则需要显式启用和配置RPS。
在禁用RPS的情况下,请使用上图中的数字:
-
netif_receive_skb
将数据传递到__netif_receive_core
。
-
__netif_receive_core
将数据传递到任何水龙头(例如PCAP)。 -
__netif_receive_core
将数据传递给已注册的协议层处理程序。在许多情况下,这就是ip_rcv
IPv4协议栈已注册的功能。
-
netif_receive_skb
将数据传递到enqueue_to_backlog
。 - 数据包被放置在每个CPU输入队列中进行处理。
- 远程CPU的NAPI结构已添加到该CPU中
poll_list
,并且IPI已排队,如果尚未运行,它将触发远程CPU上的softIRQ内核线程唤醒。 - 当
ksoftirqd
远程CPU上的内核线程运行时,它遵循上一部分中描述的相同模式,但是这次,已注册的poll
函数是process_backlog
从当前CPU的输入队列中收集数据包。 - 数据包传递到
__net_receive_skb_core
。 -
__netif_receive_core
将数据传递到任何水龙头(例如PCAP)。 -
__netif_receive_core
将数据传递给已注册的协议层处理程序。在许多情况下,这就是ip_rcv
IPv4协议栈已注册的功能。
协议栈和用户态套接字
接下来是协议栈,netfilter,berkley数据包过滤器,最后是userland套接字。该代码路径很长,但是线性且相对简单。
您可以继续遵循网络数据的详细路径。路径的简要概述是:
- 数据包由IPv4协议层使用接收
ip_rcv
。 - 执行Netfilter和路由优化。
- 发送给当前系统的数据将传递到更高级别的协议层,例如UDP。
- 分组由UDP协议层与所接收的
udp_rcv
和被排队到接收由缓冲一用户态插座udp_queue_rcv_skb
和sock_queue_rcv
。在排队到接收缓冲区之前,对伯克利包过滤器进行处理。
请注意,在此过程中,多次查询了netfilter。确切的位置可以在我们的详细演练中找到。
结论
Linux网络堆栈非常复杂,并且有许多不同的系统相互作用。调整或监视这些复杂系统的任何努力都必须努力理解它们之间的相互关系以及一个系统中的设置更改将如何影响其他系统。
这篇(很少)插图化的博客文章试图使我们更长的博客文章更易于管理和理解。