Linux待ち行列wait_queue_head_tとwait_queue_t
12964 ワード
Linux待ち行列wait_queue_head_tとwait_queue_t
August 18, 2015 7:46 PM
【整理自】http://blog.csdn.net/luoqindong/article/details/17840095 Linux待ち行列wait_queue_head_tとwait_queue_t 定義 作用 フィールド詳細 操作 を定義して初期化する.待機キューヘッダから削除待機キュー を追加する.待機イベント ウェイクアップキュー 待ち行列でスリープ
待機キューはLinuxカーネルで重要な役割を果たし、多くのLinuxドライバが待機キューに関連しています.したがって、Linuxカーネルドライバでは、待機キューを把握することが必須です.Linuxカーネルの待ち行列は,二重ループチェーンテーブルをベースとしたデータ構造である.プロセススケジューリングメカニズムと密接に結合し、コアの非同期イベント通知メカニズムを実現するために使用することができる.待ち行列ヘッダ
定義#テイギ#
ヘッダファイル:
さぎょう
カーネルでは、キューを待つのに多くの用途があります.特に、処理の中断、プロセスの同期、タイミングなどの場合です.待機キューを使用して、ブロック・プロセスの起動を実現できます.キューに基づいたデータ構造で、プロセススケジューリングメカニズムと緊密に結合され、カーネル内の非同期イベント通知メカニズム、システムリソースへのアクセスの同期などを実現することができます.
フィールドの詳細 spinlock_t lock; struct list_head task_list; 双方向ループチェーンテーブルは、待機中のプロセスを格納します.
操作
1.定義と初期化
(1)
直接定義して初期化します.
(2)
(1)に相当する定義と初期化.
(3)待ち行列の定義:
ここではwait_を定義します.queue_tタイプの変数nameをprivateをtskに設定します.wait_queue_tタイプは以下のように定義される.
ここで、
2.待機キューの追加/削除:
(1)
待機プロセスを非反発プロセスとして設定し、待機キューヘッダ(q)のキューヘッダに追加します.
この関数もadd_とwait_Queue()関数の機能は基本的に同じですが、待機中のプロセス(wait)を反発プロセスに設定します.
(2)
3.待機イベント
(1) wait_event()マクロ:
(2)
(3)
(4)
(5)
同様に
4.起動キュー:
(1)wake_up()関数:
待機キューを起動します.
(2)
とwake_up()唯一の違いはTASKを呼び覚ますしかないことです.INTERUPTIBLE状態のプロセス
(3)
これらもほぼ
5.待機キューでのスリープ:
(1) sleep_on()関数:
この関数は、待ち行列
(2)
(3)
この関数と
(4)
以上の4つの関数は、プロセスを待ち行列で眠らせているが、少し驚いているだけだ.実際に使う過程で、必要に応じて適切な関数を選んで使えばいい.例えば、FDDデータの読み書きでは、デバイスが準備ができていない場合、
August 18, 2015 7:46 PM
【整理自】http://blog.csdn.net/luoqindong/article/details/17840095
待機キューはLinuxカーネルで重要な役割を果たし、多くのLinuxドライバが待機キューに関連しています.したがって、Linuxカーネルドライバでは、待機キューを把握することが必須です.Linuxカーネルの待ち行列は,二重ループチェーンテーブルをベースとしたデータ構造である.プロセススケジューリングメカニズムと密接に結合し、コアの非同期イベント通知メカニズムを実現するために使用することができる.待ち行列ヘッダ
wait_queue_head_t)
と待ち行列エントリwait_queue_t
の2つのデータ構造があります.待機キューヘッダおよび待機キューエントリには、「コネクタ」としてlist_head
タイプのドメインが含まれています.2つのチェーンテーブルを介してtaskを待つヘッダと待機するプロセスリストをリンクします.具体的にご紹介します.定義#テイギ#
ヘッダファイル:
/include/linux/wait.h
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
さぎょう
カーネルでは、キューを待つのに多くの用途があります.特に、処理の中断、プロセスの同期、タイミングなどの場合です.待機キューを使用して、ブロック・プロセスの起動を実現できます.キューに基づいたデータ構造で、プロセススケジューリングメカニズムと緊密に結合され、カーネル内の非同期イベント通知メカニズム、システムリソースへのアクセスの同期などを実現することができます.
フィールドの詳細
task_list
と動作中に、このロックを使用して待ち行列への反発アクセスが実現される.操作
1.定義と初期化
(1)
wait_queue_head_t my_queue;
init_waitqueue_head(&my_queue);
直接定義して初期化します.
init_waitqueue_head()
関数は、スピンロックを未ロックに初期化し、キューが空の双方向ループチェーンテーブルに初期化されるのを待つ.(2)
DECLARE_WAIT_QUEUE_HEAD(my_queue);
(1)に相当する定義と初期化.
(3)待ち行列の定義:
DECLARE_WAITQUEUE(name,tsk);
ここではwait_を定義します.queue_tタイプの変数nameをprivateをtskに設定します.wait_queue_tタイプは以下のように定義される.
struct __wait_queue {
unsigned int flags;
#define WQ_FLAG_EXCLUSIVE 0x01
struct task_struct * task;
wait_queue_func_t func;
struct list_head task_list;
};
typedef struct __wait_queue wait_queue_t;
ここで、
flags
ドメインは、待機中のプロセスが反発プロセスであるか非反発プロセスであるかを示す.ここで、0は非反発プロセスであり、WQ_FLAG_EXCLUSIVE(0×01)
は反発プロセスである.待ち行列wait_queue_t
と待ち対列ヘッダwait_queue_head_t
の違いは、待ち行列が待ち行列ヘッダのメンバーであることである.すなわち、待ち行列ヘッダのtask_list
ドメインリンクのメンバは、待ち行列タイプのwait_queue_t
である.2.待機キューの追加/削除:
(1)
add_wait_queue()
関数void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
{
unsigned long flags;
wait->flags &= ~WQ_FLAG_EXCLUSIVE;
spin_lock_irqsave(&q->lock, flags);
__add_wait_queue(q, wait);
spin_unlock_irqrestore(&q->lock, flags);
}
EXPORT_SYMBOL(add_wait_queue);
待機プロセスを非反発プロセスとして設定し、待機キューヘッダ(q)のキューヘッダに追加します.
void fastcall add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait)
{
unsigned long flags;
wait->flags |= WQ_FLAG_EXCLUSIVE;
spin_lock_irqsave(&q->lock, flags);
__add_wait_queue_tail(q, wait);
spin_unlock_irqrestore(&q->lock, flags);
}
EXPORT_SYMBOL(add_wait_queue_exclusive);
この関数もadd_とwait_Queue()関数の機能は基本的に同じですが、待機中のプロセス(wait)を反発プロセスに設定します.
(2)
remove_wait_queue()
関数:void fastcall remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
{
unsigned long flags;
spin_lock_irqsave(&q->lock, flags);
__remove_wait_queue(q, wait);
spin_unlock_irqrestore(&q->lock, flags);
}
EXPORT_SYMBOL(remove_wait_queue);
3.待機イベント
(1) wait_event()マクロ:
#define wait_event(wq, condition) \ do { \ if (condition) \ break; \ __wait_event(wq, condition); \ } while (0)
#define __wait_event(wq, condition) \ do { \ DEFINE_WAIT(__wait); \ \ for (;;) { \ prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); \ if (condition) \ break; \ schedule(); \ } \ finish_wait(&wq, &__wait); \ } while (0)
(2)
wait_event_interruptible()
関数:wait_event()
との違いは、マクロを呼び出す待機中に現在のプロセスがTASK_INTERRUPTIBLE
の状態に設定されることである.起動されるたびに、まずcondition
が真であるかどうかをチェックし、真であれば戻る、そうでなければプロセスが信号によって起動された場合、-ERESTARTSYS
のエラーコードを返すことをチェックする.condition
が真の場合は0を返す.(3)
wait_event_timeout()
マクロ:wait_event()
と同様である.ただし、与えられた睡眠時間が負の場合は直ちに戻る.睡眠中に起動する場合、condition
が真である場合、残りの睡眠時間を返し、そうでない場合、所定の睡眠時間に達するまで睡眠を続け、その後、0を返す.(4)
wait_event_interruptible_timeout()
マクロ:wait_event_timeout()
と同様であるが、睡眠中に信号が遮断するとERESTARTSYS
のエラーコードが戻る.(5)
wait_event_interruptible_exclusive()
マクロ同様に
wait_event_interruptible()
と同様であるが、この睡眠のプロセスは反発プロセスである.4.起動キュー:
(1)wake_up()関数:
#define wake_up(x) __wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1, NULL)
/** * __wake_up - wake up threads blocked on a waitqueue. * @q: the waitqueue * @mode: which threads * @nr_exclusive: how many wake-one or wake-many threads to wake up */
void fastcall __wake_up(wait_queue_head_t *q, unsigned int mode,
int nr_exclusive, void *key)
{
unsigned long flags;
spin_lock_irqsave(&q->lock, flags);
__wake_up_common(q, mode, nr_exclusive, 0, key);
spin_unlock_irqrestore(&q->lock, flags);
}
待機キューを起動します.
TASK_INTERRUPTIBLE
およびTASK_UNINTERUPTIBLE
の状態にあるプロセスを起動することができ、wait_event/wait_event_timeout
とペアで使用することができる.(2)
wake_up_interruptible()
関数:#define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
とwake_up()唯一の違いはTASKを呼び覚ますしかないことです.INTERUPTIBLE状態のプロセス
wait_event_interruptible/wait_event_interruptible_timeout/ wait_event_interruptible_exclusive
とペアで使用する.(3)
#define wake_up_all(x) __wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 0, NULL)
#define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
#define wake_up_interruptible_nr(x, nr) __wake_up(x, TASK_INTERRUPTIBLE, nr, NULL)
これらもほぼ
wake_up/wake_up_interruptible
と同様である.5.待機キューでのスリープ:
(1) sleep_on()関数:
void fastcall __sched sleep_on(wait_queue_head_t *q)
{
SLEEP_ON_VAR
current->state = TASK_UNINTERRUPTIBLE;
SLEEP_ON_HEAD
schedule();
SLEEP_ON_TAIL
}
EXPORT_SYMBOL(sleep_on);
#define SLEEP_ON_VAR \
unsigned long flags; \
wait_queue_t wait; \
init_waitqueue_entry(&wait, current);
#define SLEEP_ON_HEAD \
spin_lock_irqsave(&q->lock,flags); \
__add_wait_queue(q, &wait); \
spin_unlock(&q->lock);
#define SLEEP_ON_TAIL \
spin_lock_irq(&q->lock); \
__remove_wait_queue(q, &wait); \
spin_unlock_irqrestore(&q->lock, flags);
この関数は、待ち行列
wait
を定義し、現在のプロセスを待ち行列wait
に追加し、現在のプロセスの状態をTASK_UNINTERRUPTIBLE
に設定し、待ち行列wait
を待ち行列ヘッダq
に追加する役割を果たす.その後、リソースが取得できるまで保留され、待機キューヘッダq
から起動され、待機キューヘッダから移動される.保留中にリソースを待機している間、プロセスは信号で起動できません.(2)
sleep_on_timeout()
関数:long fastcall __sched sleep_on_timeout(wait_queue_head_t *q, long timeout)
{
SLEEP_ON_VAR
current->state = TASK_UNINTERRUPTIBLE;
SLEEP_ON_HEAD
timeout = schedule_timeout(timeout);
SLEEP_ON_TAIL
return timeout;
}
(3)
interruptible_sleep_on()
関数:void fastcall __sched interruptible_sleep_on(wait_queue_head_t *q)
{
SLEEP_ON_VAR
current->state = TASK_INTERRUPTIBLE;
SLEEP_ON_HEAD
schedule();
SLEEP_ON_TAIL
}
この関数と
sleep_on()
関数の唯一の違いは、現在のプロセスの状態をTASK_INTERRUPTINLE
に設定することであり、これは、睡眠中にプロセスが信号を受信すると起動することを意味する.(4)
interruptible_sleep_on_timeout()
関数:long fastcall __sched sleep_on_timeout(wait_queue_head_t *q, long timeout)
{
SLEEP_ON_VAR
current->state = TASK_UNINTERRUPTIBLE;
SLEEP_ON_HEAD
timeout = schedule_timeout(timeout);
SLEEP_ON_TAIL
return timeout;
}
sleep_on_timeout()
関数と同様です.プロセスは、睡眠中に待機時間が到着しないうちに信号によって中断されて起動される可能性があり、待機時間が到着して起動される可能性がある.以上の4つの関数は、プロセスを待ち行列で眠らせているが、少し驚いているだけだ.実際に使う過程で、必要に応じて適切な関数を選んで使えばいい.例えば、FDDデータの読み書きでは、デバイスが準備ができていない場合、
sleep_on()
関数を呼び出してデータが読める(書き込み可能)までスリープし、シリアルポートを開いたとき、シリアルポートが閉じている場合、interruptible_sleep_on()
関数を呼び出して開くのを待つ.サウンドカード駆動では、音声データを読み取る際に、データが読み取り可能でない場合、読み取り可能になるまで十分な時間を待つ.