wait_event_interruptible(wq, condition),該函數修改task的狀態爲TASK_INTERRUPTIBLE,意味着該進程將不會繼續運行直到被喚醒,然後被添加到等待隊列wq中。
在wait_event_interruptible()中首先判斷condition是不是已經滿足,如果條件滿足則直接返回0,否則調用__wait_event_interruptible(),並用__ret來存放返回值
#define wait_event_interruptible(wq, condition) \
({ \
int __ret = 0; \
if (!(condition)) \
__wait_event_interruptible(wq, condition, __ret);\
__ret; \
})
在無限循環中,__wait_event_interruptible()將本進程置爲可中斷的掛起狀態,反覆檢查condition是否成立,如果成立則退出,如果不成立則繼續休眠;條件滿足後,即把本進程運行狀態置爲運行態
,並將__wait從等待隊列中清除掉,從而進程能夠調度運行。如果進程當前有異步信號(POSIX的),則返回-ERESTARTSYS。
#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)
以下是在linux下OV7725驅動(s3c2440camif.c)中的一個例子
定義等待隊列
wait_queue_head_t cmdqueue;(s3c2440camif.h中定義的)
初始化等待隊列
static int __init camif_probe(struct platform_device *dev)
{
......
pdev->cmdcode = CAMIF_CMD_NONE;
init_waitqueue_head(&pdev->cmdqueue);
......
}
調用等待隊列
static int start_capture(struct s3c2440camif_dev * pdev, int stream)
{
......
if (stream == 0)
{
pdev->cmdcode = CAMIF_CMD_STOP;
ret = wait_event_interruptible(pdev->cmdqueue, pdev->cmdcode == CAMIF_CMD_NONE);
}
return ret;
......
}
在中斷處理函數中喚醒等待隊列
static irqreturn_t on_camif_irq_c(int irq, void * dev)
{
......
pdev->cmdcode = CAMIF_CMD_NONE;
wake_up(&pdev->cmdqueue);
......
}
static irqreturn_t on_camif_irq_p(int irq, void * dev)
{
......
pdev->cmdcode = CAMIF_CMD_NONE;
wake_up(&pdev->cmdqueue);
......
}
在下面函數中調用start_capture()
static ssize_t camif_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
......
if(start_capture(pdev, 0) != 0)
{
return -ERESTARTSYS;
}
......
}
******************************************************************************************************************
wait_event_interruptible 宏
#define wait_event_interruptible(wq, condition) \
({ \
int __ret = 0; \
if (!(condition)) \
__wait_event_interruptible(wq, condition, __ret); \
__ret; \
})
注: C語言中{a,b, ..., x}的的值等於最後一項,即x,因此上述
宏的值是 __ret。
讀一下wait_event_interruptible()的源碼,不難發現這個函數先將
當前進程的狀態設置成TASK_INTERRUPTIBLE,然後調用schedule(),
而schedule()會將位於TASK_INTERRUPTIBLE狀態的當前進程從runqueue
隊列中刪除。從runqueue隊列中刪除的結果是,當前這個進程將不再參
與調度,除非通過其他函數將這個進程重新放入這個runqueue隊列中,
這就是wake_up()的作用了。
由於這一段代碼位於一個由condition控制的for(;;)循環中,所以當由
shedule()返回時(當然是被wake_up之後,通過其他進程的schedule()而
再次調度本進程),如果條件condition不滿足,本進程將自動再次被設
置爲TASK_INTERRUPTIBLE狀態,接下來執行schedule()的結果是再次被
從runqueue隊列中刪除。這時候就需要再次通過wake_up重新添加到
runqueue隊列中。
如此反覆,直到condition爲真的時候被wake_up.
可見,成功地喚醒一個被wait_event_interruptible()的進程,需要滿足:
在 1)condition爲真的前提下,2) 調用wake_up()。
所以,如果你僅僅修改condition,那麼只是滿足其中一個條件,這個時候,
被wait_event_interruptible()起來的進程尚未位於runqueue隊列中,因
此不會被 schedule。這個時候只要wake_up一下就立刻會重新進入運行調度。
2. 關於wait_event_interruptible的返回值
根據 wait_event_interruptible 的宏定義知:
1) 條件condition爲真時調用這個函數將直接返回0,而當前進程不會
被 wait_event_interruptible和從runqueue隊列中刪除。
2) 如果要被wait_event_interruptible的當前進程有nonblocked pending
signals, 那麼會直接返回-ERESTARTSYS(i.e. -512),當前進程不會
被wait_event_interruptible 和從runqueue隊列中刪除。
3) 其他情況下,當前進程會被正常的wait_event_interruptible,並從
runqueue隊列中刪除,進入TASK_INTERRUPTIBLE狀態退出運行調度,
直到再次被喚醒加入runqueue隊列中後而參與調度,將正常返回0。