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

Linux 內核 Hung Task 機制:D 狀態死鎖檢測原理與實戰

     編輯:Java 綜合教學

背景

Linux 進程狀態中 TASK_UNINTERRUPTIBLE(D 狀態)不響應信號,僅能由 wake_up 喚醒。常見於 mutex 等待、I/O 等待。若硬體故障或死鎖導致長期無法調度,需機制檢測並告警——即 Hung Task Detector。

核心原理(Linux 4.1.15)

內核啟動時(subsys_initcall)創建 khungtaskd 內核執行緒,週期性(預設 120 秒)掃描所有任務:

  1. 讀取任務 nvcsw + nivcsw(自願+非自願切換計數)。
  2. 若兩次掃描計數不變,且任務處於 TASK_UNINTERRUPTIBLE,判定為 Hung。
  3. 輸出堆疊回溯、寄存器、持有鎖資訊至控制台/日誌。
  4. 可配置 sysctl_hung_task_panic 觸發 Kernel Panic。

關鍵源碼流程

初始化

static int __init hung_task_init(void) {
    atomic_notifier_chain_register(&panic_notifier_list, &panic_block);
    watchdog_task = kthread_run(watchdog, NULL, "khungtaskd");
    return 0;
}

Watchdog 主循環

static int watchdog(void *dummy) {
    set_user_nice(current, 0);
    for (;;) {
        unsigned long timeout = sysctl_hung_task_timeout_secs;
        while (schedule_timeout_interruptible(timeout_jiffies(timeout)))
            timeout = sysctl_hung_task_timeout_secs;
        if (atomic_xchg(&reset_hung_task, 0))
            continue;
        check_hung_uninterruptible_tasks(timeout);
    }
}

檢測邏輯

static void check_hung_uninterruptible_tasks(unsigned long timeout) {
    if (test_taint(TAINT_DIE) || did_panic) return;
    rcu_read_lock();
    for_each_process_thread(g, t) {
        if (!max_count--) goto unlock;
        if (t->state == TASK_UNINTERRUPTIBLE)
            check_hung_task(t, timeout);
    }
unlock:
    rcu_read_unlock();
}

單任務判定

static void check_hung_task(struct task_struct *t, unsigned long timeout) {
    unsigned long switch_count = t->nvcsw + t->nivcsw;
    if (t->flags & (PF_FROZEN | PF_FREEZER_SKIP)) return;
    if (!switch_count) return;                    // 新建未調度過
    if (switch_count != t->last_switch_count) {   // 有調度發生
        t->last_switch_count = switch_count;
        return;
    }
    // 無調度超過 timeout → 告警
    trace_sched_process_hang(t);
    if (!sysctl_hung_task_warnings) return;
    if (sysctl_hung_task_warnings > 0) sysctl_hung_task_warnings--;
    pr_err("INFO: task %s:%d blocked for more than %ld seconds.\n", t->comm, t->pid, timeout);
    sched_show_task(t);
    debug_show_held_locks(t);
    touch_nmi_watchdog();
    if (sysctl_hung_task_panic) {
        trigger_all_cpu_backtrace();
        panic("hung_task: blocked tasks");
    }
}

實戰演示(樹莓派 Linux 4.1.15)

1. 內核配置

Kernel hacking → Debug Lockups and Hangs → [*] Detect Hung Tasks
Default timeout: 120s

2. 測試模組製造死鎖

DEFINE_MUTEX(dlock);
static int __init dlock_init(void) {
    mutex_lock(&dlock);
    mutex_lock(&dlock);   // 重複加鎖 → 進入 D 狀態永久等待
    return 0;
}

3. 觀測輸出

[ 360.625466] INFO: task insmod:521 blocked for more than 120 seconds.
[ 360.631878] Tainted: G O 4.1.15 #5
[ 360.637042] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
[ 360.644986] [] (__schedule) from [] (schedule+0x40/0xa4)
[ 360.652129] [] (schedule) from [] (schedule_preempt_disabled+0x18/0x1c)
[ 360.660570] [] (schedule_preempt_disabled) from [] (__mutex_lock_slowpath+0x6c/0xe4)
[ 360.670142] [] (__mutex_lock_slowpath) from [] (mutex_lock+0x44/0x48)
[ 360.678432] [] (mutex_lock) from [] (dlock_init+0x20/0x2c [dlock])
...

每 2 分鐘輸出一次,堆疊直指 mutex_lock → dlock_init,快速定位死鎖點。

總結

Hung Task 機制以極低開銷提供 D 狀態死鎖可見性,驅動開發者只需收集告警日誌即可快速定位,建議生產環境啟用並調整 hung_task_timeout_secs、hung_task_warnings 避免日誌刷屏。

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