關於函數wait_event_interruptible(wq, condition)

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。 






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章