Skip to content

第一百三十六章:中断下半部

合道期

涉及内核源码:

林小源跟在那位中断武神身后,沿着山路继续前行。忽然,武神停下脚步,指向山腰处的一道裂缝。

裂缝将一块巨石劈成两半。上半部分悬在半空中,闪烁着紫色的电弧——那是硬中断的标志。下半部分沉在地底,散发着柔和的蓝光。

"中断上下半部。"武神说,"这是内核处理中断的核心设计。"

他指着上半部分:"上半部就是硬中断处理——CPU 暂停一切,跳转到处理函数,处理最紧急的事:读设备状态、清除中断标志、把数据拷到安全的地方。然后立刻退出,越快越好。"

"那耗时的工作呢?"林小源问。

"留给下半部。"武神指向地底那团蓝光,"下半部在中断处理结束后执行,处理那些不那么紧急但耗时的工作——数据处理、协议栈逻辑、内存分配。"

c
/*
 * 中断下半部的机制:
 *
 * 1. softirq (软中断)
 *    静态定义,编译时确定
 *    类型有限(约 10 种)
 *    性能最好
 *    例: NET_RX_SOFTIRQ, TIMER_SOFTIRQ
 *
 * 2. tasklet
 *    基于 softirq 实现
 *    可以动态创建
 *    使用更简单
 *    同一 tasklet 不能并发
 *
 * 3. 工作队列
 *    在进程上下文中执行
 *    可以休眠
 *    最灵活
 *
 * 选择:
 *   需要最高性能 → softirq
 *   简单延迟处理 → tasklet
 *   需要休眠 → 工作队列
 */

printf("=== 中断下半部 — 延迟处理 ===\n\n");

printf("中断上下半部:\n\n");
printf("上半部 (硬中断):\n");
printf("  处理紧急工作\n");
printf("  读取设备状态\n");
printf("  清除中断标志\n");
printf("  调度下半部\n\n");
printf("下半部:\n");
printf("  处理耗时工作\n");
printf("  数据处理\n");
printf("  协议栈逻辑\n\n");

printf("--- 下半部机制 ---\n\n");

printf("1. softirq:\n");
printf("   静态定义\n");
printf("   性能最好\n");
printf("   NET_RX_SOFTIRQ — 网络收包\n");
printf("   TIMER_SOFTIRQ — 定时器\n\n");

printf("2. tasklet:\n");
printf("   基于 softirq\n");
printf("   可以动态创建\n");
printf("   同一 tasklet 不能并发\n\n");

printf("3. 工作队列:\n");
printf("   在进程上下文中执行\n");
printf("   可以休眠\n");
printf("   最灵活\n\n");

printf("--- 选择 ---\n");
printf("需要最高性能 → softirq\n");
printf("简单延迟处理 → tasklet\n");
printf("需要休眠 → 工作队列\n\n");

printf("--- 网络中断示例 ---\n");
printf("上半部:\n");
printf("  网卡中断\n");
printf("  读取状态\n");
printf("  调度 NET_RX_SOFTIRQ\n\n");
printf("下半部:\n");
printf("  处理收到的数据包\n");
printf("  传递给协议栈\n");

"下半部有三种实现方式。"武神蹲下身,在地上画了三个圆圈。

他指着第一个圆圈:"softirq——软中断。它是最底层的下半部机制,在中断上下文中执行。类型在编译时就确定了,总共只有十来种—— 处理网络收包, 处理定时器。因为是静态定义的,性能最好,没有运行时开销。"

"那它有什么限制?"

"类型太少,你不能随便加新的 softirq。"武神说,"而且它在中断上下文中执行,同样不能休眠。"

他又指着第二个圆圈:"tasklet——基于 softirq 实现,但更灵活。你可以动态创建任意数量的 tasklet。不过有个限制——同一个 tasklet 不能在多个 CPU 上同时执行。如果你有多个 CPU,同一个 tasklet 的不同实例会被串行化。"

"是为了避免并发问题?"

"对。tasklet 的设计目标就是简单安全——你不用考虑锁的问题,因为同一个 tasklet 保证不会并发。"

武神指着第三个圆圈,这个圆圈的颜色跟前两个不同——它散发着温暖的橙色光芒。

"工作队列。"武神说,"它跟前两个有本质区别——工作队列在进程上下文中执行,不是中断上下文。"

"进程上下文?"林小源眼睛一亮,"那就可以休眠了?"

"对。"武神点头,"工作队列中的函数可以休眠、可以获取 mutex、可以分配大块内存、可以做文件操作。它是最灵活的下半部机制。"

"那为什么不用工作队列处理所有下半部?"

"因为上下文切换有开销。"武神说,"softirq 和 tasklet 在中断返回时直接执行,不需要调度。工作队列需要唤醒内核线程,涉及上下文切换,延迟更高。所以——"

他站起身,拍了拍手上的泥土:"需要最高性能,用 softirq。需要简单延迟处理,用 tasklet。需要休眠或者做复杂操作,用工作队列。没有万能的选择,只有合适的权衡。"

林小源看着那三个圆圈,心中记下了这个设计:上半部处理紧急的事,下半部处理耗时的事;softirq 最快但最僵硬,工作队列最灵活但最慢,tasklet 居中。一切取决于场景。


道藏笔记

内核启示

中断下半部处理耗时的工作。

下半部机制:

  • softirq — 静态定义,性能最好
  • tasklet — 动态创建,使用简单
  • 工作队列 — 进程上下文,可以休眠

选择原则:

  • 最高性能 → softirq
  • 简单延迟 → tasklet
  • 需要休眠 → 工作队列

下半部是"延迟"的智慧——把耗时的工作留到后面。


破关试炼

中断下半部之试

中断下半部一章里,被用来把不紧急工作延后执行的传统机制是什么?

答对后才能继续滑动和进入下一章。

以修仙之名,悟内核之道