在 Linux 內核開發中,wait_event_interruptible 是實現進程休眠與喚醒的核心接口之一。本文通過源碼宏展開,深入剖析其工作原理、狀態流轉及信號處理機制。
#define wait_event_interruptible(wq, condition) \
({ \
int __ret = 0; \
if (!(condition)) \
__wait_event_interruptible(wq, condition, __ret); \
__ret; \
})
condition,若為真則直接返回 0(無需休眠)。__wait_event_interruptible,並通過 __ret 傳回錯誤碼。({ ... }))使宏可作為表達式使用。
#define __wait_event_interruptible(wq, condition, ret) \
do { \
DEFINE_WAIT(__wait); \
for (;;) { \
prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \
if (condition) \
break; \
if (!signal_pending(current)) { \
schedule(); \
continue; \
} \
ret = -ERESTARTSYS; \
break; \
} \
finish_wait(&wq, &__wait); \
} while (0)
wait_queue_entry 結構,初始化為當前進程(current)和喚醒函數 autoremove_wake_function。TASK_INTERRUPTIBLE(可中斷睡眠)。__wait 加入等待隊列 wq。condition 成立,跳出循環。signal_pending(current) 檢查是否有待處理信號。
schedule() 讓出 CPU,進入睡眠。ret = -ERESTARTSYS,跳出循環(系統調用將被中斷並返回 -ERESTARTSYS,由內核決定是否重啟)。TASK_RUNNING,並從等待隊列移除 __wait。
TASK_RUNNING
│
▼ prepare_to_wait()
TASK_INTERRUPTIBLE ──schedule()──► 睡眠(等待隊列)
│ │
│ condition=true │ signal_pending()
▼ ▼
break 循環 ret=-ERESTARTSYS
│ │
└───────► finish_wait() ──► TASK_RUNNING
| 接口 | 睡眠狀態 | 可被信號喚醒 | 返回值 |
|---|---|---|---|
| wait_event | TASK_UNINTERRUPTIBLE | 否 | 無(void) |
| wait_event_interruptible | TASK_INTERRUPTIBLE | 是 | 0 / -ERESTARTSYS |
| wait_event_timeout | TASK_UNINTERRUPTIBLE | 否 | 剩餘 jiffies(0 為超時) |
| wait_event_interruptible_timeout | TASK_INTERRUPTIBLE | 是 | 同上 |
-ERESTARTSYS 時,驅動程序通常應返回該錯誤碼讓系統調用層處理重啟邏輯。wait_event_interruptible_exclusive 或自定義喚醒函數。wait_event_interruptible 通過「檢查條件 → 準備等待 → 睡眠調度 → 被喚醒重新檢查」的循環,實現了高效、可中斷的進程同步。理解其宏展開後的邏輯,有助於編寫正確的驅動程序並排查休眠/喚醒相關問題。