在Linux驅動程序中,可以使用等待隊列(wait queue)來實現阻塞進程的喚醒。等待很早就作為一個基本的功能單位出現在Linux內核中,它以隊列為基礎數據結構,與進程調度機制緊密結合,能夠用於實現內核中的異步事件通知機制。
我們從它的使用範例著手,看看等待隊列是如何實現異步信號功能的。以下代碼節選自kernel/printk.c。
DECLARE_WAIT_QUEUE_HEAD(log_wait); // 初始化等待隊列頭log_wait
static DEFINE_SPINLOCK(logbuf_lock); // 定義自旋鎖logbuf_lock
int do_syslog(int type, char __user *buf, int len)
{
... ... ...
err = wait_event_interruptible(log_wait, (log_start - log_end)); // 等待
/*
// 我們先來看看linux/wait.h中定義的wait_event_interruptible()的原型
#define wait_event_interruptible(wq, condition)
({
int __ret = 0;
if ( (!condition) )
__wait_event_interruptible(wq, condition, __ret);
__ret;
})
// wait_event_interruptible()將等待隊列加入第一個參數wq為等待隊列頭的等待隊列鏈表中,
// 當condition滿足時,wait_event_interruptible()會立即返回,否則,阻塞等待condition滿足。
// 與之類似的還有wait_event(),不同點在於wait_event_interruptible()可以被信號喚醒。
// __wait_event_interruptible()原型如下:
#define __wait_event_interruptible(wq, condition, __ret)
do {
DEFINE_WAIT(__wait); // 定義等待隊列__wait
for(;;) {
prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE);
// prepare_to_wait()的作用是:將等待隊列__wait加入以wq為等待隊列的等待隊列
// 鏈表中,並且將進程狀態設置為TASK_INTERRUPTIBLE
if (condition)
break; // 如果condition滿足則跳出
if (!signal_pending(current)) { // 如果不是被信號喚醒
ret = schedule_timeout(ret);
if (!ret)
break;
continue;
}
ret = - ERESTARTSYS;
break;
schedule(); // 放棄CPU,調度其它進程執行
}
finish_wait(&wq, &__wait);
// finish_wait()的作用是,將等待隊列__wait從等待隊列頭wq指向的等待隊列鏈表中移除,
// 並且將進程狀態設置為TASK_RUNNING
}while(0)
*/
... ... ...
spin_lock_irq(&lock_wait); // 鎖定
... ... ...
log_start++; // log_start自加,使得(log_start - log_end)這個等待隊列的condition滿足
... ... ...
spin_unlock_irq(&lock_wait); // 解鎖
... ... ...
}
void wake_up_klogd(void)
{
... ... ...
wake_up_interruptible(&lock_wait); //喚醒等待隊列
}
void release_console_sem(void)
{
... ... ...
spin_lock_irqsave(&lock_wait); // 鎖定
wake_klogd = log_start - log_end;
... ... ...
spin_unlock_irqrestore(&lock_wait); // 解鎖
... ... ...
if (wake_klogd)
wake_up_klogd(); // 如果wake_klogd非負,則調用wake_up_klogd()來喚醒等待隊列
... ... ...
}
我們最後再總結一下等待隊列wait_event_interruptible():
在wait_event_interruptible()中首先判斷conditon是不是已經滿足,如果是則直接返回0,否則調用__wait_event_interruptible(),並用__ret來存放返回值。__wait_event_interruptible()首先將定義並初始化一個wait_queue_t變量__wait,其中數據為當前進程狀態current(struct task_struct),並把__wait加入等待隊列。在無限循環中,__wait_event_interruptible()將本進程置為可中斷的掛起狀態,反覆檢查condition是否成立,如果成立則退出,如果不成立則繼續休眠;條件滿足後,即把本進程運行狀態置為運行態,並把__wait從等待隊列中清除,從而進程能夠調度運行。
掛起的進程並不會自動轉入運行的,因此,還需要一個喚醒動作,這個動作由wake_up_interruptible()完成,它將遍歷作為參數傳入的lock_wait等待隊列,將其中所有的元素(通常都是task_struct)置為運行態,從而可被調度。
沒有留言:
張貼留言