核心等待佇列的介紹

2021-10-02 15:13:21 字數 3104 閱讀 9784

目錄

一.背景和意義

二.資料結構分析

三.等待佇列的操作

3.1 典型應用

3.2 將等待佇列項插入等待佇列

3.3 將等待佇列從等待佇列中刪除

3.4 將等待佇列從等待佇列中刪除

在實際程式設計中,我們會經常碰到這種場景:程序p需要等待條件c的成立,才能繼續執行某個動作。例如,當串列埠沒有資料可以讀取時,我們可以通過輪詢的方式,等到有資料來的時候,串列埠程式再去讀取。但是這種方式顯得比較笨拙,影響cpu的效能。因此,核心中提供了等待佇列的方式,即可以將程序p先掛到等待佇列q(wait_queue)上去,並將程序的狀態由running切換為睡眠狀態,主動讓出cpu,直到條件滿足時,由核心呼叫wake_up()介面,自動喚醒q上的所有程序,這樣程序p就繼續執行。

因此,wait_queue的實現,能夠提高整個系統以及cpu執行的效率。

wait_queue使用到的資料結構很簡單,就是使用核心鍊錶,將等待同一事件的所有程序串聯起來。

第乙個資料結構是等待佇列頭:

struct wait_queue_head ;

typedef struct wait_queue_head wait_queue_head_t;

等待佇列頭可以用以下api進行初始化:

wait_queue_head_t wait_queue_head;

init_waitqueue_head(&wait_queue_head);

第二個資料結構是等待佇列的佇列元素:

struct wait_queue_entry ;
這裡,睡眠程序被分成兩種,flags為1代表的是互斥程序,這些程序在喚醒時會被由選擇地喚醒;flags為0代表的是非互斥程序,喚醒時,核心將喚醒所有的非互斥程序。

當多個程序等待互斥資源時,同時喚醒所有程序將會導致又一次的競爭,而只有乙個程序會獲得互斥資源,因此其他程序又必須重新睡眠。為了避免以上的情況,才定義了互斥程序的概念,並使用flags來區分互斥程序和非互斥程序。

func欄位是程序被喚醒的方式,缺省會被初始化為default_wake_function()。

可以使用以下api對等待佇列的佇列元素進行動態的初始化:

struct wait_queue_entry wait_entry;

init_wait(&wait_entry);

或者使用靜態的初始化方法:

define_wait(wait_entry);
以上兩種方式的結果相同,以init_wait 為例:

#define define_wait_func(name, function)                    \

struct wait_queue_entry name =

#define define_wait(name) define_wait_func(name, autoremove_wake_function)

/* 1. 定義乙個等待佇列項 */

define_wait(wait);

/* 2. 將等待佇列項wait插入等待佇列wq中,並將程序的狀態設定為interruptible */

prepare_to_wait(&wq, &wait, task_interruptible);

.../* 3. 如果期望的條件不滿足,則主動讓出cpu,切換其他程序執行 */

if (!condition)

schedule();

/* * 4. 程序被喚醒後,繼續向下執行,將程序的狀態設定為執行狀態,並將等待佇列項wait從等待佇列wq中刪除。

*/finish_wait(&wq, &wait);

一般,定義並初始化完等待佇列後,需要通過prepare_to_wait() 或者 prepare_to_wait_exclusive ()函式,將程序的狀態改變,並將其加入對應的等待佇列中:

void

prepare_to_wait(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry, int state)

export_symbol(prepare_to_wait);

void

prepare_to_wait_exclusive(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry, int state)

export_symbol(prepare_to_wait_exclusive);

從以上原始碼可以看出,prepare_to_wait() 或者 prepare_to_wait_exclusive ()函式有以下區別:

1. prepare_to_wait_exclusive ()函式會將等待佇列項對應的flags的wq_flag_exclusive使能,也就是說,通過prepare_to_wait_exclusive ()函式新增的等待佇列項,對應的程序被設定為互斥程序;而通過prepare_to_wait()新增的等待佇列項,對應的程序被設定為非互斥程序。

2. 通過prepare_to_wait()新增的非互斥等待佇列項,會被新增至等待佇列的隊頭,而通過 prepare_to_wait_exclusive ()函式新增的互斥項,會被新增至等待佇列的隊尾。

二者都會改變當前程序的狀態,由傳入的引數state 決定。

當程序被喚醒後,一般會直接呼叫finish_wait函式,將程序的狀態重新設定為running,並將該程序對應的等待佇列項從等待佇列中刪除:

void finish_wait(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry)

}

喚醒等待佇列上的程序:

一般,可以通過wake_up, wake_up_all等api對喚醒佇列上的程序進行喚醒。wake_up將會喚醒所有的非互斥程序,而只喚醒乙個互斥程序; wake_up_all將會喚醒所有的非互斥程序,和所有的互斥程序。其他api可以參看核心原始碼。

核心等待佇列 筆記

可以用等待佇列來實現程序的阻塞 操作方法 1 定義等待佇列 wait queue head t my queue 2 初始化等待佇列 int waitqueue head my queue 3 定義並初始化等待佇列 declare wait queue head my queue 4 有條件睡眠 1...

Linux核心等待佇列

在linux驅動程式設計中,可以使用等待佇列來實現程序的阻塞,等待佇列可看作儲存程序的容器,在阻塞程序時,將程序放入等待佇列,當喚醒程序時,從等待等列中取出程序。linux 2.6核心提供了如下關於等待佇列的操作 1 定義等待佇列 wait queue head t my queue 2 初始化等待...

核心等待佇列的使用

1.定義等待佇列 wait queue head t my queue 2.初始化等待佇列 init waitqueue head my queue 3.定義並同時初始化佇列 declare wait queue head my queue 4.利用等待佇列使程序睡眠 wait event queu...