待ち合わせ処理とは
非同期による遅延処理 や後述する 割込み処理 と、元の処理において待ち合わせを行い、同期を取る為の仕組みがLinuxカーネルにおいて用意されています。照度センサドライバでは使用されていませんが、 代表的な物として、wait_eventやwait_for_completionがあるので、それぞれについて説明します。
wait_event の使い方
wait_eventは、特定の条件が満たされるまでプロセスを停止する仕組みです。条件の変化を監視するのに使用可能です。wait_eventは、以下の通り定義されています。
kernel/mediatek/4.4/include/linux/wait.h
/**
* wait_event - sleep until a condition gets true
* @wq: the waitqueue to wait on
* @condition: a C expression for the event to wait for
*
* The process is put to sleep (TASK_UNINTERRUPTIBLE) until the
* @condition evaluates to true. The @condition is checked each time
* the waitqueue @wq is woken up.
*
* wake_up() has to be called after changing any variable that could
* change the result of the wait condition.
*/
#define wait_event(wq, condition)
第1引数には、struct wait_queue_head_t型のポインタを指定します。これは、init_waitqueue_headで事前に初期化しておきます。
第2引数には、停止を解除して再開する条件を指定します。
init_waitqueue_headは同ファイルに、以下の通り定義されています。
#define init_waitqueue_head(q) \
do { \
static struct lock_class_key __key; \
\
__init_waitqueue_head((q), #q, &__key); \
} while (0)
その他、jiffiesによるtimeoutを指定できる関数等があります。
#define wait_event_timeout(wq, condition, timeout)
また、wait_eventによる停止中は、そのプロセスはスリープ状態となっているので、条件を満たす値にした時に通常、wake_upも合わせて呼び出します。
#define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)
第1引数には、wait_eventにも使用したstruct wait_queue_head_t型のポインタを指定します。
照度センサドライバでは使用されてないので、I2Cホストドライバである以下から使用例を記載します。
kernel/mediatek/4.4/drivers/i2c/busses/i2c-mtk.c
mt_i2c_do_transfer関数
i2c->trans_stop = false;
<省略>
tmo = wait_event_timeout(i2c->wait, i2c->trans_stop, tmo);
mt_i2c_irq関数
i2c->trans_stop = true;
<省略>
wake_up(&i2c->wait);
データ信号の送信をmt_i2c_do_transferで行った後に停止した上で、後述する 割り込み をmt_i2c_irqで受け取った後に、再開する為に使用されてます。
wait_for_completion の使い方
wait_for_completionは、プロセスが他の処理の完了を待つ為の仕組みです。他の処理によりcomplete関数が呼び出されるまでプロセスを停止します。wait_for_completionは、以下の通り定義されています。
kernel/mediatek/4.4/kernel/sched/completion.c
/**
* wait_for_completion: - waits for completion of a task
* @x: holds the state of this particular completion
*
* This waits to be signaled for completion of a specific task. It is NOT
* interruptible and there is no timeout.
*
* See also similar routines (i.e. wait_for_completion_timeout()) with timeout
* and interrupt capability. Also see complete().
*/
void __sched wait_for_completion(struct completion *x)
引数には、struct completion型のポインタを指定します。この値は以下の通り定義されている、init_completionやreinit_completionによって事前に初期化しておく必要があります。
kernel/mediatek/4.4/linux/completion.h
/**
* init_completion - Initialize a dynamically allocated completion
* @x: pointer to completion structure that is to be initialized
*
* This inline function will initialize a dynamically created completion
* structure.
*/
static inline void init_completion(struct completion *x)
/**
* reinit_completion - reinitialize a completion structure
* @x: pointer to completion structure that is to be reinitialized
*
* This inline function should be used to reinitialize a completion structure so it can
* be reused. This is especially important after complete_all() is used.
*/
static inline void reinit_completion(struct completion *x)
wait_for_completionの他に、jiffiesによるtimeoutを指定できる関数等があります。
unsigned long __sched
wait_for_completion_timeout(struct completion *x, unsigned long timeout)
wait_for_completion等による停止を解除して再開するには、以下のcomplete関数を用います。
/**
* complete: - signals a single thread waiting on this completion
* @x: holds the state of this particular completion
*
* This will wake up a single thread waiting on this completion. Threads will be
* awakened in the same order in which they were queued.
*
* See also complete_all(), wait_for_completion() and related routines.
*
* It may be assumed that this function implies a write memory barrier before
* changing the task state if and only if any tasks are woken up.
*/
void complete(struct completion *x)
引数には、wait_for_completion等にも指定したstruct completion型のポインタを指定します。
照度センサドライバでは使用されてないので、I2Cコアドライバである以下から使用例を記載します
kernel/mediatek/4.4/drivers/i2c/i2c-core.c
i2c_del_adapter関数
init_completion(&adap->dev_released);
device_unregister(&adap->dev);
wait_for_completion(&adap->dev_released);
i2c_adapter_dev_release関数
complete(&adap->dev_released);
device_unregister関数から非同期で実行されるi2c_adapter_dev_release関数について、完了した後に呼び出すcompleteのタイミングで同期を取った後に、再開する為に使用されてます。
最後に
どちらを使用しても実現できることが多いですが、wait_eventは条件に基づいて待つ為、イベントに依存する待ち合わせに適しています。対して、wait_for_completionは特定のタスクが完了するのを待つのに使用され、非同期処理の終了との待ち合わせに適しています。
コメント