天天看點

等待隊列等待隊列1. 基礎介紹2 等待隊列的部分概念3. 核心提供的隊列操作API總結:

等待隊列

1. 基礎介紹

等待隊列很早就作為一個基本的功能機關存在linux核心中,它以隊列為基礎資料結構,與程序排程機制緊密配合,能夠用于實作核心中的異步事件通知機制。等待隊列也可以用來同步對系統資源的通路。在使用時将其當做成一個普通隊列資料結構,隻不過等待隊列是若幹個休眠程序的集合,且核心自己實作了此隊列初始化隊列、入隊列、出隊列的一系列API,在使用時隻需要調用系統的API即可。

簡單的了解等待隊列: 一個休眠程序的隊列,等待特定事件的喚醒。

2 等待隊列的部分概念

等待隊列頭:

等待隊列頭,顧名思義是等待隊列的頭部。一個等待隊列有一個等待隊列頭,其他程序喚醒時,隻将一個等待隊列頭的第一個休眠程序喚醒。

等待隊列項:

等待隊列頭就是一個等待隊列的頭部,每個通路裝置的程序都是一個隊列項,當裝置不可用的時候就要将這些程序對應的等待隊列項添加到等待隊列裡面。

假設一個場景: 全年級同學在操場集合領書,不同的班級在一隊(等待隊列)。當叫到哪個班級時,該班級的第一位同學上來領書(隊列喚醒),沒叫到名字的同學原地等待(休眠程序,等待隊列項)。此時班級就是一個等待隊列頭。相同班級的同學組成的隊列就是等待隊列。

3. 核心提供的隊列操作API

3.1 等待隊列頭:

結構體原型:

struct __wait_queue_head {
    spinlock_t lock;        struct list_head task_list;};typedef struct __wait_queue_head wait_queue_head_t;           

複制

等待隊列頭初始化:

#if 0//靜态初始化DECLARE_WAIT_QUEUE_HEAD(name);
#else//動态初始化wait_queue_head_t my_queue;
init_waitqueue_head(&my_queue);#endif           

複制

3.2等待隊列項:

結構體原型:

struct __wait_queue {
    unsigned int flags;    void *private;    wait_queue_func_t func;
};typedef struct __wait_queue wait_queue_t;           

複制

等待隊列項定義:

DECLARE_WAITQUEUE(name, tsk)           

複制

3.3 添加移除等待隊列項

目前程序,先将其休眠,然後加入到等待隊列,便于其他程序喚醒;程序在喚醒後,就需要移出等待隊列。

void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);           

複制

3.4 睡眠

自動睡眠

Linux 核心中睡眠的最簡單方式是一個宏定義, 稱為 wait_event(有幾個變體); 它結合了處理睡眠的細節和程序在等待的條件的檢查. wait_event 的形式是:

queue 是等待隊列頭,condition 是條件,如果調用 wait_event 前 condition == 0 ,則調用 wait_event 之後,目前程序就會休眠,反之不會休眠。

wait_event(queue, condition);               将目前程序程序的狀态設定為 TASK_UNINTERRUPTIBLE   ,然後 schedule()

wait_event_interruptible(queue, condition);                          TASK_INTERRUPTIBLE     ,然後 schedule()

wait_event_timeout(queue, condition, timeout);                       TASK_UNINTERRUPTIBLE   ,然後 schedule_timeout()

wait_event_interruptible_timeout(queue, condition, timeout);         TASK_INTERRUPTIBLE     ,然後 schedule_timeout()           

複制

TASK_INTERRUPTIBLE 與 TASK_UNINTERRUPTIBLE 差別在于,它的休眠是否會被信号中斷,别的程序發來一個信号比如 kill ,TASK_INTERRUPTIBLE 就會醒來去處理。然而 TASK_UNINTERRUPTIBLE 不會。schedule(),程序排程,而schedule_timeout()進行排程之後,一定時間後自動喚醒(逾時喚醒),若喚醒後,condition仍然為假,則繼續睡眠,反之執行。

手動睡眠

DECLARE_WAITQUEUE(name, tsk)  建立一個等待隊列:
tsk一般為目前進行current. 這個宏定義并初始化一個名為name的等待隊列.
 将等待隊列頭 加入/移除 等待隊列:       void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);       void add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait);       void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
設定程序狀态:
        set_current_state(TASK_INTERRUPTIBLE) 等
程序排程: 
        schedule() 或者 schedule_timeout()           

複制

還是用自動睡眠較為友善。

3.4 喚醒

當程序進入等待隊列休眠時,其他程序可以主動叫等待隊列頭喚醒首個等待隊列項。若為自動睡眠,先判斷condition:若為真則執行,否則繼續睡眠。

void wake_up(wait_queue_head_t *q)void wake_up_interruptible(wait_queue_head_t *q)           

複制

總結:

筆者在最開始學習時,感覺其與信号量的作用似乎相似。最後經過思考,總結出不同。

信号量 : 其作用在于保護臨界區,即臨界區被占用時,其他通路該臨界區的被動線程休眠。待信号量釋放以後,其次的線程才可以通路。

等待隊列: 是将目前程序先休眠,直到該程序被其他程序喚醒才可執行,且有逾時喚醒功能。

2020-06-27