首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Linux 內核線程深度解析(一):ksoftirqd 與 events 工作隊列機制

     編輯:Java 綜合教學

引言:內核線程是系統穩定運行的基石

Linux 內核啟動過程中會創建數十個內核線程,它們在後台默默處理中斷底半部、延遲任務、內存回收等關鍵工作。本文聚焦兩個最「知名」且最常被提及的線程——ksoftirqd(軟中斷處理)與 events/kworker(工作隊列執行器),通過源碼級分析揭示其運行機制與設計哲學。

第一部分:ksoftirqd —— 軟中斷的「緩衝池」

1.1 為什麼需要 ksoftirqd?

Linux 中斷處理分為「硬中斷(Top Half)」與「軟中斷」:

  • 硬中斷:優先級最高,執行極短、極快,只做必要的硬件狀態保存與軟中斷調度。
  • 軟中斷:優先級次之,可被硬中斷打斷,處理耗時較長的 I/O 協議棧邏輯(網絡收發、塊設備完成等)。

問題:高負載下(如萬兆網卡收包),軟中斷頻繁觸發且可能重新調度自身,導致用戶進程長期無法調度(「餓死」)。

解決方案:內核規定——軟中斷處理函數中若再次調度同類軟中斷,不立即執行,而是標記 pending,喚醒 per-CPU 的 ksoftirqd 線程在進程上下文中以最低優先級(nice=19)處理。這兼顧了:

  1. 用戶進程不會餓死(ksoftirqd 低優先級)
  2. 軟中斷最終一定會被處理(線程會被調度)

1.2 核心數據結構與入口

  • 每 CPU 一個線程,命名 ksoftirqd/0、ksoftirqd/1…
  • 入口函數 ksoftirqd(void *__bind_cpu),主循環調用 do_softirq()
  • do_softirq() 又是 irq_exit()(硬中斷返回)與 ksoftirqd 共同調用的統一入口

1.3 關鍵源碼流程解讀

irq_exit() —— 硬中斷返回時的軟中斷檢查

void irq_exit(void) {
    // ... 更新 preempt_count 表示退出硬中斷上下文 ...
    if (!in_interrupt() && local_softirq_pending())
        invoke_softirq();  // 實際調用 do_softirq()
}

in_interrupt() 通過 preempt_count 的 bit 位判斷是否仍處於硬中斷/軟中斷/NMI 上下文,防止重入。

do_softirq() —— 統一處理入口

asmlinkage void do_softirq(void) {
    if (in_interrupt()) return;  // 若在中斷上下文,交給 ksoftirqd
    local_irq_save(flags);
    if (local_softirq_pending())
        __do_softirq();
    local_irq_restore(flags);
}

__do_softirq() —— 核心處理循環(最多 10 次重啟)

#define MAX_SOFTIRQ_RESTART 10
void __do_softirq(void) {
    __local_bh_disable();  // 屏蔽本地軟中斷,防止嵌套
    local_irq_enable();    // 開啟硬中斷,允許硬中斷搶占

    restart:
    set_softirq_pending(0);  // 清 pending 位(需在關中斷下)
    // 遍歷 32 位 pending bitmap,調用對應 action(h)
    do { if (pending & 1) h->action(h); h++; pending >>= 1; } while (pending);

    local_irq_disable();   // 關硬中斷,重新檢查 pending
    pending = local_softirq_pending();
    if (pending && --max_restart) goto restart;  // 最多 10 次

    if (pending) wakeup_softirqd();  // 還有剩餘,喚醒 ksoftirqd 線程
    _local_bh_enable();  // 開啟軟中斷
}

設計精妙點:

  • 開硬中斷執行 action,保證硬中斷響應延遲
  • 但關軟中斷,防止同類軟中斷嵌套
  • 硬中斷產生的新軟中斷只能下一輪或交給 ksoftirqd
  • 10 次重啟閾值防止佔用過多 CPU,超限轉交 ksoftirqd

ksoftirqd() 線程主循環

static int ksoftirqd(void *__bind_cpu) {
    set_user_nice(current, 19);  // 最低優先級
    set_current_state(TASK_INTERRUPTIBLE);
    while (!kthread_should_stop()) {
        preempt_disable();
        if (!local_softirq_pending()) {
            preempt_enable_no_resched();
            schedule();  // 無事可做,睡眠讓出 CPU
            preempt_disable();
        }
        __set_current_state(TASK_RUNNING);
        while (local_softirq_pending()) {
            if (cpu_is_offline((long)__bind_cpu)) goto wait_to_die;
            do_softirq();  // 處理 pending 軟中斷
            preempt_enable_no_resched();
            cond_resched();  // 主動讓調度,防止長期佔用 CPU
            preempt_disable();
        }
        preempt_enable();
        set_current_state(TASK_INTERRUPTIBLE);
    }
    // ... 退出清理 ...
}

關鍵點:cond_resched() 讓出 CPU,確保高優先級任務可搶占;preempt_disable/enable 保護鏈表操作原子性。

1.4 tasklet 與 ksoftirqd 的關係

tasklet 基於 TASKLET_SOFTIRQ/HI_SOFTIRQ 實現,屬於軟中斷一種。當 tasklet 大量調度時,同樣會觸發 ksoftirqd 處理,表現為 ksoftirqd CPU 佔用率升高。


第二部分:events / kworker —— 工作隊列的執行引擎

2.1 Workqueue 機制概覽

Workqueue 是內核提供的「可睡眠的異步執行框架」,解決:

  • 中斷上下文不能睡眠,但驅動常需睡眠操作(申請內存、等待 I/O、鎖)
  • 簡化內核線程創建,自動綁定 CPU 實現並行

核心數據結構:

  1. cpu_workqueue_struct:每 CPU 一個,綁定一個內核線程,維護任務鏈表 work_list 等待隊列 waitq
  2. work_struct:任務描述符,含回調函數 func、私有數據 data、延遲定時器 timer

2.2 默認工作隊列:keventd_wq 與 events/n 線程

系統啟動早期創建 keventd_wq,每 CPU 對應一個線程,命名 events/0、events/1…(新內核稱 kworker/uX:Y)。驅動可直接使用 schedule_work() 將任務投遞至此隊列,無需自建線程。

2.3 工作隊列創建 API 對比

API線程模型適用場景
create_workqueue(name)每 CPU 一個線程高並行、CPU 密集型任務
create_singlethread_workqueue(name)單一全局線程串行化、順序敏感、低頻任務

自建隊列需調用 destroy_workqueue() 釋放。

2.4 任務調度接口速查

接口隊列特性
schedule_work(&work)keventd_wq立即調度
schedule_delayed_work(&work, delay)keventd_wq延遲調度(jiffies)
queue_work(wq, &work)自定義 wq立即調度
queue_delayed_work(wq, &work, delay)自定義 wq延遲調度

2.5 內核線程主循環邏輯(簡化)

while (!kthread_should_stop()) {
    // 從 work_list 取任務
    work = list_first_entry(&cwq->work_list);
    if (!work) {
        sleep_on(&cwq->waitq);  // 隊列空,睡眠等待
        continue;
    }
    // 執行回調
    work->func(work);
}

總結與對比

維度ksoftirqdevents/kworker
觸發源硬中斷調度軟中斷、tasklet驅動/子系統主動提交 work
上下文進程上下文(可睡眠)進程上下文(可睡眠)
優先級nice=19(最低)普通內核線程優先級
並行模型每 CPU 固定一個每 CPU 可多個(多隊列)
典型用途網絡收發、塊設備完成、定時器驅動初始化、固件加載、異步 I/O 完成

理解這兩類線程,能幫助你在 top/perf 中快速定位「高 CPU 佔用是軟中斷風暴還是工作隊列積壓」,並在驅動開發時正確選擇「軟中斷/tasklet」還是「workqueue」。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved