网络软中断:数据包接收与传输处理机制详解
1. 软中断概述
当特定位对应的软中断(softIRQ)被触发时,意味着该软中断需要被处理。我们会从
softirq_vec
中调用对应的软中断处理函数,即
softirq_vec[iteration].action()
,其中
iteration
是在循环中遍历以找到该置位位的次数。可以通过调用
open_softirq()
来注册软中断处理函数,它会在
softirq_vec[32]
中为对应的软中断位创建处理函数的入口。在网络设备初始化(
net_dev_init()
)时,会通过调用
open_softirq()
来注册用于接收(Rx)和发送(Tx)的网络软中断。
2. 数据包接收与延迟处理
当网络接口在其 DMA 缓冲区中完整接收到一个帧时,会触发设备的 Rx 中断。Rx 处理程序的任务是将帧从 Rx DMA 缓冲区中取出,并将其发送到上层进行处理。为了避免处理数据包花费过多时间,Rx 处理程序会将数据包排队到特定 CPU 的
soft_net
输入队列
softnet_data[this_cpu] → input_pkt_queue
中(通过调用
netif_rx()
),并通过调用
netif_rx_schedule()
来调度与当前 CPU 的软网络队列关联的设备,以便稍后进行处理。这会在该 CPU 上触发网络 Rx 软中断
NET_RX_SOFTIRQ
,该 CPU 将在稍后处理接收到的数据包。
netif_rx_schedule()
的具体操作如下:
1. 调用
netif_rx_schedule_prep()
检查设备是否已被调度或处于关闭状态。检查设备是否处于运行状态(
dev → state
应设置为
__LINK_STATE_START
)且尚未被调度(
dev → state
不应设置为
__LINK_STATE_RX_SCHED
)。如果两个条件都满足,
netif_rx_schedule_prep()
返回
true
。
2. 每个 CPU 只有一个网络设备被调度来处理接收到的数据包,即
softnet_data[this_cpu] → blog_dev
。
3. 如果该设备已经被调度,则不再重复调度并返回;否则,调用
__netif_rx_schedule()
进行调度。
__netif_rx_schedule()
的操作流程如下:
1. 找到当前 CPU 的 ID。
2. 将作为参数传递的网络设备(
softnet_data[this_cpu] → blog_dev
)添加到该 CPU 的软网络轮询列表(
softnet_data[cpu].poll_list
)中。
3. 检查设备的配额是否已用完。如果用完,则将现有配额按默认值(
dev → weight
)增加;否则,将设备配额重新初始化为默认值。设备配额限制了 Rx 软中断在一次操作中可以在给定 CPU 上处理的数据包数量。
4. 最后,通过调用
__cpu_raise_softirq()
在该 CPU 上触发网络 Rx 软中断。
在单 CPU 机器上,即使有多个网络接口,所有传入的数据包都会排队到同一个 CPU 的
softnet_data[this_cpu] → input_pkt_queue
中。而在 SMP 机器上,每个 CPU 都有自己的设备轮询列表,同一设备的数据包可能会排队到不同 CPU 的
softnet_data
输入队列中。
下面是数据包接收与调度延迟处理的流程图:
graph TD
A[帧接收完成] --> B[触发 Rx 中断]
B --> C[Rx 处理程序]
C --> D[调用 netif_rx()]
D --> E[数据包入队 softnet_data[this_cpu] → input_pkt_queue]
C --> F[调用 netif_rx_schedule()]
F --> G{设备是否已调度?}
G -- 是 --> H[返回]
G -- 否 --> I[调用 __netif_rx_schedule()]
I --> J[添加设备到轮询列表]
I --> K{设备配额是否用完?}
K -- 是 --> L[增加配额]
K -- 否 --> M[重置配额]
I --> N[触发网络 Rx 软中断]
3. 网络 Rx 软中断处理
网络 Rx 软中断在
do_softirq()
中处理,其处理函数是
net_rx_action()
。
net_rx_action()
的主要任务是从软网络轮询列表中取出设备,并逐个处理 CPU 的软网络输入队列中的数据包,直到时间配额或处理的数据包数量用完。
net_rx_action()
的具体步骤如下:
1. 获取 CPU ID。
2. 获取该 CPU 的
softnet_data
数组元素。
3. 初始化与配额相关的变量。
budget
初始化为
netdev_max_backlog
(全局变量,初始值为 300),
start_time
初始化为当前 CPU 的
jiffies
。
4. 在访问轮询列表和
jiffies
之前,禁用本地 CPU 的中断。因为
jiffies
在定时器中断中被修改,而轮询列表在网卡的 Rx 中断中被修改。
5. 检查是否已用完处理 Rx 软中断分配的预算。如果是,且轮询列表中还有其他设备需要处理,则重新调度该设备,稍后再处理,启用本地中断并返回。
6. 启用本地 CPU 的中断后,从轮询列表中访问下一个设备。
7. 检查设备的配额。如果配额已用完,禁用本地中断,将设备从轮询列表中移除,添加到轮询列表的末尾,调整设备配额,然后重新开始处理下一个设备。
8. 如果配额未用完(
dev → quota > 0
),调用
dev → poll()
,默认情况下指向
process_backlog()
。如果
dev → poll()
返回 0,则处理下一个设备;否则,重复上述步骤。
9. 当轮询列表中的所有设备都处理完毕后,启用本地中断并返回。
process_backlog()
用于处理 CPU 的
softnet_data
输入队列中排队的数据包。其操作步骤如下:
1. 计算数据包处理的配额,取传入的
budget
和设备配额的最小值。
2. 获取当前 CPU 要处理的
softnet_data
队列。
3. 将当前的
jiffies
值存储在本地变量中,以便计算处理时间。
4. 禁用本地 CPU 的中断,尝试从队列中取出下一个要处理的数据包。
5. 如果队列中没有数据包,则结束处理,减少设备配额和
budget
,从 CPU 的轮询列表中删除该设备,并清除调度标志。
6. 如果队列中还有数据包,禁用中断从队列中出队,启用本地中断,调用
netif_receive_skb()
对数据包进行进一步处理。
7. 增加表示已处理数据包数量的本地变量
work
。
8. 检查是否已用完处理积压数据包的配额或时间。如果是,不删除设备,不重置调度标志,更新设备配额和
budget
,返回 -1。
通过全局配额和设备配额的设置,
net_rx_action()
和
process_backlog()
可以协同工作,在处理积压数据包时,既能保证网络 Rx 软中断有足够的时间处理积压队列,又不会在网络流量较大时完全占用 CPU。
下面是网络 Rx 软中断处理的流程图:
graph TD
A[开始 net_rx_action()] --> B[获取 CPU ID]
B --> C[获取 softnet_data 元素]
C --> D[初始化配额变量]
D --> E[禁用本地中断]
E --> F{预算是否用完?}
F -- 是 --> G[重新调度设备并返回]
F -- 否 --> H[启用中断访问下一个设备]
H --> I{设备配额是否用完?}
I -- 是 --> J[调整设备并重新开始]
I -- 否 --> K[调用 dev → poll()]
K --> L{dev → poll() 返回 0?}
L -- 是 --> H
L -- 否 --> J
J --> H
H --> M{轮询列表处理完?}
M -- 是 --> N[启用中断返回]
M -- 否 --> H
4. 数据包传输与软中断
在数据包传输过程中,需要使用软中断的原因是,不能总是确保设备随时准备好通过网络传输数据包,并且同一设备不能被多个 CPU 同时访问以进行帧传输。在 SMP 机器上,如果每个 CPU 都运行相同的驱动代码来访问硬件设备进行帧传输,其他 CPU 要么需要等待,要么返回数据包无法传输的指示,这会严重影响性能。因此,为了解决这个问题,会将帧重新排队到设备队列中,将设备调度到 CPU 的输出队列,并在该 CPU 上触发 Tx 软中断,以便稍后处理这些帧。
数据包传输从
dev_queue_xmit()
开始,该函数接收一个完整的帧,并使用设备排队例程(在
Qdisc
结构中指定,即
dev → qdisc
)将帧排队到设备队列中。具体步骤如下:
1. 持有设备的队列锁(禁用底半部),以确保在将数据包入队设备队列时的安全性。
2. 调用与传出数据包算法特定的入队函数(
dev → qdisc → enqueue()
)将数据包排队。
3. 调用
qdisc_run()
来处理设备队列中排队的数据包,在调用时持有队列锁,以防止多个 CPU 同时处理同一设备。
qdisc_run()
会持续循环,直到设备关闭或无法再处理队列中的数据包。
qdisc_restart()
用于处理设备队列中的数据包,其操作步骤如下:
1. 获取设备的
Qdisc
结构指针。
2. 使用特定的出队函数(
q → dequeue()
)从队列中取出下一个数据包。
3. 如果所有数据包都已处理,返回队列长度;否则,继续处理下一个数据包。
4. 获取设备的传输锁,释放队列锁。
5. 检查设备是否关闭。如果未关闭,调用特定于硬件的设备传输例程开始数据包传输。
6. 如果数据包传输成功,设置锁所有者为 -1,释放设备传输锁,持有设备队列锁,返回 -1。
7. 如果出现错误(如无法获取设备传输锁或无法传输数据包),停止设备的传输处理,调度设备稍后处理,并触发网络 Tx 软中断。
netif_schedule()
用于调度设备稍后处理,它会检查设备是否开启,如果开启,则调用
__netif_schedule()
进行实际调度。
__netif_schedule()
会检查设备是否已被调度,如果未调度,则将设备排队到 CPU 的输出队列,并触发网络 Tx 软中断。
网络 Tx 软中断的回调函数是
net_tx_action()
,它会处理触发软中断的 CPU 的
output_queue
。具体步骤如下:
1. 获取 CPU ID。
2. 检查完成队列
softnet_data[cpu].completion_queue
,将其中已处理(传输)的数据包(
sk_buffs
)逐个出队并释放。
3. 处理 CPU 的
output_queue
中的设备。对于每个设备,清除其调度状态,尝试获取设备的队列锁。如果获取成功,调用
qdisc_restart()
处理设备队列中的下一个数据包;否则,调度设备稍后处理,并触发软中断。
下面是数据包传输的流程图:
graph TD
A[完整数据包] --> B[调用 dev_queue_xmit()]
B --> C[持有队列锁]
C --> D[调用 dev → qdisc → enqueue()]
D --> E[数据包入队设备队列]
B --> F[调用 qdisc_run()]
F --> G{设备是否关闭?}
G -- 否 --> H{是否有数据包可处理?}
H -- 是 --> I[调用 qdisc_restart()]
I --> J[获取 Qdisc 指针]
J --> K[调用 q → dequeue()]
K --> L{所有数据包处理完?}
L -- 是 --> M[返回队列长度]
L -- 否 --> N[获取传输锁]
N --> O[释放队列锁]
O --> P{设备是否关闭?}
P -- 否 --> Q[开始数据包传输]
Q --> R{传输是否成功?}
R -- 是 --> S[设置锁所有者为 -1]
S --> T[释放传输锁]
T --> U[持有队列锁]
U --> V[返回 -1]
R -- 否 --> W[调度设备稍后处理]
W --> X[触发网络 Tx 软中断]
H -- 否 --> Y[结束处理]
G -- 是 --> Y
F --> Z[释放队列锁]
综上所述,网络软中断机制在数据包的接收和传输过程中起着关键作用,通过合理的调度和配额管理,确保了网络设备在多 CPU 环境下的高效运行,避免了资源的过度占用和性能的下降。
网络软中断:数据包接收与传输处理机制详解
5. 软中断处理机制的优势总结
网络软中断机制在数据包处理过程中展现出了多方面的显著优势,以下是详细总结:
-
高效的资源利用
:通过将数据包的处理延迟到软中断处理阶段,避免了在中断处理程序中进行复杂的操作,从而减少了中断处理的时间,提高了 CPU 的利用率。例如,在 Rx 处理中,Rx 处理程序快速将数据包排队,然后调度设备稍后处理,使得 CPU 可以在中断处理后继续执行其他任务。
-
多 CPU 环境的适应性
:在 SMP 机器上,软中断机制允许数据包在不同 CPU 之间进行合理分配和处理。同一设备的数据包可以排队到不同 CPU 的输入或输出队列中,由相应 CPU 的软中断进行处理,避免了多个 CPU 同时访问同一设备时的冲突,提高了系统的并发处理能力。
-
配额管理的灵活性
:全局配额和设备配额的设置为数据包处理提供了灵活的控制机制。在处理积压数据包时,既能保证网络软中断有足够的时间处理队列,又不会在网络流量较大时完全占用 CPU,确保了系统的稳定性和公平性。
| 优势 | 描述 |
|---|---|
| 高效的资源利用 | 减少中断处理时间,提高 CPU 利用率 |
| 多 CPU 环境的适应性 | 避免多 CPU 访问同一设备的冲突,提高并发处理能力 |
| 配额管理的灵活性 | 保证网络软中断处理时间,避免 CPU 过度占用 |
6. 软中断处理流程对比
为了更清晰地理解网络软中断在数据包接收和传输过程中的不同处理流程,下面对 Rx 和 Tx 软中断的处理流程进行对比:
| 处理阶段 | Rx 软中断 | Tx 软中断 |
|---|---|---|
| 触发条件 | 网络接口在 DMA 缓冲区完整接收到帧,触发 Rx 中断 | 数据包传输过程中出现设备不可用或传输失败等情况,触发 Tx 软中断 |
| 处理函数 |
net_rx_action()
|
net_tx_action()
|
| 主要操作 | 从软网络轮询列表中取出设备,处理 CPU 软网络输入队列中的数据包 |
处理 CPU 的
output_queue
中的设备,包括释放完成队列中的数据包和处理设备队列中的数据包
|
| 配额管理 | 使用全局配额和设备配额控制处理的数据包数量和时间 | 无明确的配额管理,但通过设备队列锁和传输锁确保设备的串行访问 |
通过对比可以看出,Rx 软中断主要关注数据包的接收和积压处理,而 Tx 软中断则侧重于数据包的传输和设备的调度。
7. 软中断机制的潜在问题与应对策略
虽然网络软中断机制带来了诸多优势,但在实际应用中也可能面临一些潜在问题,以下是对这些问题的分析及相应的应对策略:
-
队列拥塞问题
:当网络流量过大时,
softnet_data
的输入或输出队列可能会出现拥塞,导致数据包处理延迟增加。应对策略包括调整全局配额和设备配额,限制队列的增长速度;同时,可以采用流量控制算法,对进入队列的数据包进行限速。
-
中断处理延迟
:如果软中断处理函数的执行时间过长,可能会导致其他中断的处理延迟,影响系统的实时性。为了避免这种情况,可以优化软中断处理函数的代码,减少不必要的操作;同时,可以将一些复杂的处理任务拆分成多个小任务,分阶段进行处理。
-
多 CPU 同步问题
:在 SMP 机器上,多个 CPU 同时处理软中断时可能会出现同步问题,如数据竞争和死锁。为了解决这些问题,需要使用合适的同步机制,如锁和信号量,确保对共享资源的访问是串行的。
8. 未来发展趋势展望
随着网络技术的不断发展,网络软中断机制也将面临新的挑战和机遇。以下是对其未来发展趋势的展望:
-
智能化调度
:未来的软中断机制可能会引入智能化的调度算法,根据网络流量的实时情况和设备的状态,动态调整软中断的处理顺序和配额分配,提高系统的整体性能。
-
硬件加速
:随着硬件技术的进步,可能会出现专门用于处理网络软中断的硬件模块,将部分软中断处理任务卸载到硬件上,进一步提高处理效率。
-
与新兴技术的融合
:网络软中断机制可能会与新兴技术,如人工智能和机器学习相结合,实现对网络流量的智能分析和预测,提前做好资源分配和调度,提高网络的可靠性和稳定性。
综上所述,网络软中断机制在数据包的接收和传输过程中起着至关重要的作用。通过深入理解其工作原理、优势和潜在问题,并关注未来的发展趋势,我们可以更好地优化网络系统的性能,确保网络的高效运行。在实际应用中,需要根据具体的网络环境和需求,合理配置软中断机制,充分发挥其优势,同时采取有效的应对策略,解决可能出现的问题。
graph LR
A[网络软中断机制] --> B[优势]
A --> C[潜在问题]
A --> D[未来趋势]
B --> B1[高效资源利用]
B --> B2[多 CPU 适应性]
B --> B3[配额管理灵活]
C --> C1[队列拥塞]
C --> C2[中断处理延迟]
C --> C3[多 CPU 同步问题]
D --> D1[智能化调度]
D --> D2[硬件加速]
D --> D3[与新兴技术融合]
以上就是关于网络软中断机制在数据包接收和传输处理方面的详细介绍,希望能为相关领域的研究和实践提供有益的参考。
超级会员免费看
1149

被折叠的 条评论
为什么被折叠?



