中斷處理是內核響應硬件事件的核心路徑。為平衡「中斷響應延遲」與「系統吞吐量」,Linux 將中斷處理劃分為上半部與下半部。本文基於 Linux 2.6.22 內核,從使用者視角深度剖析三大下半部機制——Softirq(軟中斷)、Tasklet、Workqueue(工作隊列) 的設計哲學、核心數據結構、調度時機及選型指南。
早期內核(2.2/2.4)使用 Bottom Half (BH):靜態 32 個槽位、全局鎖串行執行、不支持 SMP 並行。Linux 2.6 引入 Softirq 解決 SMP 可擴展性問題,核心思想:「誰觸發,誰執行」,每個 CPU 獨立處理本地觸發的軟中斷,無需全局鎖競爭。
// include/linux/interrupt.h
struct softirq_action {
void (*action)(struct softirq_action *); // 處理函數
void *data; // 私有數據
};
// 全局軟中斷向量表(32 槽位)
static struct softirq_action softirq_vec[NR_SOFTIRQS];
| 常量 | 值 | 用途 |
|---|---|---|
| HI_SOFTIRQ | 0 | 高優先級 tasklet |
| TIMER_SOFTIRQ | 1 | 定時器超時處理 |
| NET_TX_SOFTIRQ | 2 | 網絡發送 |
| NET_RX_SOFTIRQ | 3 | 網絡接收(核心協議棧入口) |
| BLOCK_SOFTIRQ | 4 | 塊設備 I/O 完成 |
| TASKLET_SOFTIRQ | 5 | 普通 tasklet 處理 |
| SCHED_SOFTIRQ | 6 | 調度器負載均衡 |
| HRTIMER_SOFTIRQ | 7 | 高精度定時器(可選) |
// 註冊(通常在子系統初始化時調用)
open_softirq(NET_RX_SOFTIRQ, net_rx_action);
// 觸發(中斷上半部調用)
raise_softirq(NET_RX_SOFTIRQ);
// 執行入口(多個觸發點)
do_softirq(); // 1. 硬中斷退出 irq_exit()
// 2. ksoftirqd 內核線程
// 3. 顯式調用(如 local_bh_enable())
若軟中斷持續高頻觸發,do_softirq() 會長期佔用 CPU,導致用戶進程餓死。內核啟動每個 CPU 一個 ksoftirqd/n 線程,當檢測到軟中斷積壓時,將處理推遲到該線程中執行,讓出調度機會。
TASKLET_SOFTIRQ / HI_SOFTIRQ 實現。DECLARE_TASKLET(name, func, data) 或 tasklet_init()。kworker/uX:Y),允許睡眠、持有鎖、訪問用戶內存。DECLARE_WORK(name, func) 或 INIT_WORK()。schedule_work()(共享默認 workqueue)、queue_work()(自定義 workqueue)。schedule_delayed_work()、queue_delayed_work()。| 特性 | Softirq | Tasklet | Workqueue |
|---|---|---|---|
| 上下文 | 軟中斷(原子) | 軟中斷(原子) | 進程(可睡眠) |
| 並發性 | 同類多 CPU 並行 | 同類串行,異類並行 | 完全並行(多線程) |
| 延遲 | 極低(微秒級) | 低 | 較高(毫秒級,需調度) |
| 可睡眠 | 否 | 否 | 是 |
| 優先級 | 最高 | 高/普通 | 可調(nice 值) |
| 典型用途 | 網絡收發、塊層、定時器 | 驅動中斷後處理 | 文件 I/O、內存回收、用戶態交互 |
NGSA 採用統一的「中斷線程」模型:中斷處理函數直接在內核線程中運行,無顯式上/下半部之分。優點是編程模型簡單、天然可睡眠;缺點是中斷延遲略高、線程切換開銷大。Linux 通過 irq_exit() 集中調用 do_softirq(),為移植到類 NGSA 架構預留了清晰接口。