Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

Linux 核心等待隊列機制深度解析:數據結構、睡眠流程與喚醒原理

編輯:Linux 教學

等待隊列是 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 ──────────────┴───────────────────────────┘

睡眠流程:wait_event_interruptible 宏展開

最常用的可中斷睡眠宏:


#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)

關鍵步驟解析

1. 定義等待項:DEFINE_WAIT


#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:默認喚醒後自動從隊列移除

2. 入隊並設置狀態:prepare_to_wait


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);
}

3. 條件檢查與調度

  • 檢查 condition,滿足則跳出
  • 檢查信號,有信號返回 -ERESTARTSYS
  • 調用 schedule() 切換進程,CPU 執行其他任務
  • 被喚醒後循環重新檢查 condition(防止虛假喚醒/搶佔)

4. 清理:finish_wait


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);
    }
}

喚醒流程:wake_up 系列


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;  // 獨占模式:喚醒指定數量後停止
    }
}

默認喚醒回調:autoremove_wake_function


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;
}

獨占 vs 非獨占模式

特性非獨占獨占
flags0WQ_FLAG_EXCLUSIVE
喚醒行為喚醒所有等待進程喚醒指定數量(通常 1 個)
適用場景事件通知、廣播資源競爭、鎖、信號量
驚群效應可能產生避免驚群

獨占等待 API


#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)

核心 API 速查表

宏/函數功能可中斷獨占
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 喚醒語義,是核心開發與驅動編程的必備內功。

Copyright © Windows教程網 All Rights Reserved