目錄
一.背景和意義
二.資料結構分析
三.等待佇列的操作
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...