等待隊列是 Linux 核心實現進程同步、阻塞等待的核心機制,廣泛應用於驅動程序、文件系統、進程調度等子系統。本文系統剖析等待隊列的數據結構、睡眠/喚醒流程、獨占/非獨占模式及核心 API 實現邏輯。
struct __wait_queue_head {
spinlock_t lock; // 保護鏈表的自旋鎖
struct list_head task_list; // 等待隊列鏈表頭
};
typedef struct __wait_queue_head wait_queue_head_t;
struct __wait_queue {
unsigned int flags;
#define WQ_FLAG_EXCLUSIVE 0x01 // 獨占標記
void *private; // 通常指向 task_struct
wait_queue_func_t func; // 喚醒回調函數
struct list_head task_list; // 鏈表節點
};
typedef struct __wait_queue wait_queue_t;
wait_queue_head_t (head)
│
├── lock (spinlock_t)
│
└── task_list (list_head) ◄──┐
│
┌─────────────────────────┼─────────────────────────┐
▼ ▼ ▼
wait_queue_t (item1) wait_queue_t (item2) wait_queue_t (item3)
│ │ │
├── private = task_A ├── private = task_B ├── private = task_C
├── func = autoremove... ├── func = autoremove... ├── func = custom_wake
├── flags = 0 (非獨占) ├── flags = WQ_FLAG_EXCLUSIVE ├── flags = 0
└── task_list ──────────────┴───────────────────────────┘
最常用的可中斷睡眠宏:
#define wait_event_interruptible(wq, condition) \
({ \
int __ret = 0; \
if (!(condition)) \
__wait_event_interruptible(wq, condition, __ret); \
__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)
#define DEFINE_WAIT(name) \
wait_queue_t name = { \
.private = current, \
.func = autoremove_wake_function, \
.task_list = LIST_HEAD_INIT((name).task_list), \
}
private = current:綁定當前進程func = autoremove_wake_function:默認喚醒後自動從隊列移除
void fastcall prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
{
unsigned long flags;
wait->flags &= ~WQ_FLAG_EXCLUSIVE; // 默認非獨占
spin_lock_irqsave(&q->lock, flags);
if (list_empty(&wait->task_list))
__add_wait_queue(q, wait); // 加入隊列尾部
if (is_sync_wait(wait))
set_current_state(state); // 設置 TASK_INTERRUPTIBLE
spin_unlock_irqrestore(&q->lock, flags);
}
condition,滿足則跳出-ERESTARTSYSschedule() 切換進程,CPU 執行其他任務condition(防止虛假喚醒/搶佔)
void fastcall finish_wait(wait_queue_head_t *q, wait_queue_t *wait)
{
unsigned long flags;
__set_current_state(TASK_RUNNING);
if (!list_empty_careful(&wait->task_list)) {
spin_lock_irqsave(&q->lock, flags);
list_del_init(&wait->task_list); // 從隊列移除
spin_unlock_irqrestore(&q->lock, flags);
}
}
void wake_up(wait_queue_head_t *q)
{
__wake_up(q, TASK_NORMAL, 1, NULL);
}
void __wake_up(wait_queue_head_t *q, unsigned int mode, int nr_exclusive, void *key)
{
struct list_head *tmp, *next;
list_for_each_safe(tmp, next, &q->task_list) {
wait_queue_t *curr = list_entry(tmp, wait_queue_t, task_list);
unsigned flags = curr->flags;
// 調用回調函數,返回 true 表示移除該等待項
if (curr->func(curr, mode, 1, key) &&
(flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
break; // 獨占模式:喚醒指定數量後停止
}
}
int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key)
{
int ret = default_wake_function(wait, mode, sync, key);
if (ret)
list_del_init(&wait->task_list); // 喚醒成功後自動移除
return ret;
}
| 特性 | 非獨占 | 獨占 |
|---|---|---|
| flags | 0 | WQ_FLAG_EXCLUSIVE |
| 喚醒行為 | 喚醒所有等待進程 | 喚醒指定數量(通常 1 個) |
| 適用場景 | 事件通知、廣播 | 資源競爭、鎖、信號量 |
| 驚群效應 | 可能產生 | 避免驚群 |
#define wait_event_exclusive(wq, condition) \
__wait_event_exclusive(wq, condition)
#define __wait_event_exclusive(wq, condition) \
do { \
DEFINE_WAIT_EXCLUSIVE(__wait); \
for (;;) { \
prepare_to_wait_exclusive(&wq, &__wait, TASK_UNINTERRUPTIBLE); \
if (condition) break; \
schedule(); \
} \
finish_wait(&wq, &__wait); \
} while (0)
| 宏/函數 | 功能 | 可中斷 | 獨占 |
|---|---|---|---|
wait_event(wq, cond) | 不可中斷睡眠 | 否 | 否 |
wait_event_interruptible(wq, cond) | 可中斷睡眠 | 是 | 否 |
wait_event_timeout(wq, cond, timeout) | 帶超時睡眠 | 否 | 否 |
wait_event_interruptible_timeout | 可中斷帶超時 | 是 | 否 |
wait_event_exclusive(wq, cond) | 獨占睡眠 | 否 | 是 |
wake_up(wq) | 喚醒所有 | - | - |
wake_up_nr(wq, nr) | 喚醒 nr 個獨占 | - | - |
wake_up_all(wq) | 喚醒所有(同 wake_up) | - | - |
wake_up_interruptible(wq) | 喚醒可中斷進程 | - | - |
等待隊列以「隊列頭 + 隊列項」雙向鏈表為骨架,配合自旋鎖保護、進程狀態機(TASK_RUNNING/INTERRUPTIBLE/UNINTERRUPTIBLE)、調度器(schedule())與回調函數機制,實現了高效、靈活的進程同步原語。掌握 prepare_to_wait → schedule → finish_wait 三部曲與 wake_up 喚醒語義,是核心開發與驅動編程的必備內功。