Linux 內核完成接口

Linux 內核裏面有一個函數wait_for_completion,這是一個內核同步機制的函數,同步機制如果是早期的讀者應該看過我發的文章,如果沒有看過的可以看看Linux 專輯文章裏面找找。

既然是同步機制,主要的工作就是調用了這個函數,程序就會等另外的事件完成之後再繼續下面的工作。

wait_for_completion 結構體

/*
 * struct completion - structure used to maintain state for a "completion"
 *
 * This is the opaque structure used to maintain the state for a "completion".
 * Completions currently use a FIFO to queue threads that have to wait for
 * the "completion" event.
 *
 * See also:  complete(), wait_for_completion() (and friends _timeout,
 * _interruptible, _interruptible_timeout, and _killable), init_completion(),
 * reinit_completion(), and macros DECLARE_COMPLETION(),
 * DECLARE_COMPLETION_ONSTACK().
 */
struct completion {
 unsigned int done;
 wait_queue_head_t wait;
};

結構體裏面就兩個東西,一個是 done,一個是隊列wait。

還有他相關的函數

complete()
wait_for_completion() 
init_completion()
reinit_completion()
DECLARE_COMPLETION()
DECLARE_COMPLETION_ONSTACK().

如何使用?

先聲明

struct completion bl_ready;
init_completion(&ts->bl_ready);

需要等待退出的線程

static irqreturn_t cyttsp_irq(int irq, void *handle)
{
 struct cyttsp *ts = handle;
 int error;

 /*退出的時候會設置爲CY_BL_STATE*/
 /*在這裏做判斷*/
 if (unlikely(ts->state == CY_BL_STATE)) {
  complete(&ts->bl_ready);
  goto out;
 }
}

退出時候調用

static int cyttsp_soft_reset(struct cyttsp *ts)
{
 unsigned long timeout;
 int retval;

 /* wait for interrupt to set ready completion */
 reinit_completion(&ts->bl_ready);
 
 /*退出的時候會設置爲 CY_BL_STATE*/
 ts->state = CY_BL_STATE;

 enable_irq(ts->irq);

 retval = ttsp_send_command(ts, CY_SOFT_RESET_MODE);
 if (retval)
  goto out;

 timeout = wait_for_completion_timeout(&ts->bl_ready,
   msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX));
 retval = timeout ? 0 : -EIO;

out:
 ts->state = CY_IDLE_STATE;
 disable_irq(ts->irq);
 return retval;
}

裏面的實現細節

三個函數的流程

init_completion 初始化

我們看init 初始化的代碼

static inline void init_completion(struct completion *x)
{
 x->done = 0;
 init_waitqueue_head(&x->wait);
}

static inline void reinit_completion(struct completion *x)
{
 x->done = 0;
}

初始化的時候,把done這個變量設置爲 0 。

wait_for_completion_timeout 的調用流程

static inline long __sched
do_wait_for_common(struct completion *x,
     long (*action)(long), long timeout, int state)
{
 /*如果done是0就跑到if裏面去,也就說明我們等的事件還沒完成*/
 if (!x->done) {
  DECLARE_WAITQUEUE(wait, current);

  __add_wait_queue_tail_exclusive(&x->wait, &wait);
  do {
   if (signal_pending_state(state, current)) {
    timeout = -ERESTARTSYS;
    break;
   }
   __set_current_state(state);
   spin_unlock_irq(&x->wait.lock);
   timeout = action(timeout);/*休眠timeout時間等待*/
   spin_lock_irq(&x->wait.lock);
  } while (!x->done && timeout);/*如果事件完成了就提出*/
  __remove_wait_queue(&x->wait, &wait);
  if (!x->done)
   return timeout;
 }
 x->done--;
 return timeout ?: 1;
}

內核裏面有非常多這樣的小代碼,我覺得看這樣的代碼非常有意思,你需要不斷的去揣摩它,品味它。

這段代碼的主要工作就是在超時時間內判斷done的值已經大於0,然後退出,退出的時候把done 減去1。

總結

文章裏面的實例是從一個touch驅動裏面提取出來的,它的目的是爲了reset觸摸屏的時候,確保中斷線程已經完全退出了,在中斷線程裏面有操作touch I2C的操作,如果reset的時候,線程還沒有跑完,驅動就會可能收到I2C報錯的異常。

 error = request_threaded_irq(ts->irq, NULL, cyttsp_irq,
         IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
         pdata->name, ts);

內核完成補丁

這份補丁是用來修復do_wait_for_common 這個函數的,看註釋的作用就是用一行代碼代替了幾行代碼。內核專家做了修正,貼出來方便大家加深理解。

Change do_wait_for_common() to use signal_pending_state() instead of
open coding.

Signed-off-by: Oleg Nesterov <oleg@xxxxxxxxxx>

--- 26-rc2/kernel/sched.c~1_SPS_WAIT_FOR 2008-07-22 18:36:58.000000000 +0400
+++ 26-rc2/kernel/sched.c 2008-07-24 19:54:12.000000000 +0400
@@ -4735,10 +4735,7 @@ do_wait_for_common(struct completion *x,
wait.flags |= WQ_FLAG_EXCLUSIVE;
__add_wait_queue_tail(&x->wait, &wait);
do {
- if ((state == TASK_INTERRUPTIBLE &&
- signal_pending(current)) ||
- (state == TASK_KILLABLE &&
- fatal_signal_pending(current))) {
+ if (signal_pending_state(state, current)) {
timeout = -ERESTARTSYS;
break;
}

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

推薦閱讀:

專輯|Linux文章彙總

專輯|程序人生

嵌入式Linux

微信掃描二維碼,關注我的公衆號

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