Go to main content
Oracle® Solaris 11.3 デバイスドライバの記述

印刷ビューの終了

更新: 2016 年 11 月
 
 

スレッド同期

共有データの保護に加えて、ドライバでは複数スレッドの実行を同期することがしばしば必要になります。

スレッド同期における条件変数

条件変数はスレッド同期の標準的な形式です。条件変数は mutex と組み合わせて使用するように設計されています。mutex を関連付けて使用することにより、条件を原子的にチェックできることが保証されます。また、条件の変更や、条件が変更されたことのシグナルを取りこぼさずに、関連付けられた条件変数でスレッドをブロックできることも保証されます。

condvar(9F) 関数には次のものがあります。

cv_broadcast(9F)

条件変数で待機しているすべてのスレッドにシグナルを送信します。

cv_destroy(9F)

条件変数を破棄します。

cv_init(9F)

条件変数を初期化します。

cv_signal(9F)

条件変数で待機している 1 つのスレッドにシグナルを送信します。

cv_timedwait(9F)

条件、タイムアウト、またはシグナルを待機します。スレッドがシグナルを受信できないを参照してください。

cv_timedwait_sig(9F)

条件またはタイムアウトを待機します。

cv_wait(9F)

条件を待機します。

cv_wait_sig(9F)

条件を待機します。シグナルを受信した場合は 0 を返します。スレッドがシグナルを受信できないを参照してください。

条件変数の初期化

条件ごとに kcondvar_t 型の条件変数を宣言します。条件変数は通常、ドライバのソフト状態構造体で宣言されます。それぞれの条件変数を初期化するには、cv_init(9F) を使用します。条件変数は通常、mutex と同様に attach(9E) の時点で初期化されます。条件変数の初期化の一般的な例を次に示します。

cv_init(&xsp->cv, NULL, CV_DRIVER, NULL);

条件変数の初期化のより完全な例については、ドライバの自動構成を参照してください。

条件の待機

条件変数を使用するには、条件を待機するコードパスで次の手順に従います。

  1. 条件をガードしている mutex を取得します。

  2. 条件を評価します。

  3. 評価結果でスレッドの再開が許可されない場合、cv_wait(9F) を使用して、その条件で現在のスレッドをブロックします。cv_wait(9F) 関数はスレッドをブロックする前に mutex を解放し、戻る前に mutex を再取得します。cv_wait(9F) から戻ったら、評価を繰り返します。

  4. 評価によりスレッドの再開が許可されたら、条件を新しい値に設定します。たとえば、デバイスフラグをビジーに設定します。

  5. mutex を解放します。

条件のシグナル送信

条件のシグナルを送信するには、コードパスで次の手順に従います。

  1. 条件をガードしている mutex を取得します。

  2. 条件を設定します。

  3. cv_broadcast(9F) を使用して、ブロックされているスレッドにシグナルを送信します。

  4. mutex を解放します。

次の例では、mutex と条件変数に加えてビジーフラグを使用します。転送を開始する前に、デバイスがビジー状態でなくなるまで read(9E) ルーチンを強制的に待機させます。

使用例 1  mutex と条件変数の使用
static int
xxread(dev_t dev, struct uio *uiop, cred_t *credp)
{
        struct xxstate *xsp;
        /* ... */
        mutex_enter(&xsp->mu);
        while (xsp->busy)
                cv_wait(&xsp->cv, &xsp->mu);
        xsp->busy = 1;
        mutex_exit(&xsp->mu);
        /* perform the data access */
}

static uint_t
xxintr(caddr_t arg)
{
        struct xxstate *xsp = (struct xxstate *)arg;
        mutex_enter(&xsp->mu);
        xsp->busy = 0;
        cv_broadcast(&xsp->cv);
        mutex_exit(&xsp->mu);
}

cv_wait() および cv_timedwait() 関数

cv_wait(9F) によってスレッドが条件でブロックされ、その条件が発生しない場合、スレッドは待機し続けることになります。その状況を避けるには、別のスレッドに依存して復帰処理を実行する cv_timedwait(9F) を使用します。cv_timedwait() は絶対待ち時間を引数に取ります。 指定された時間に達してもイベントが発生しなかった場合、cv_timedwait()-1 を返します。条件が満たされた場合、cv_timedwait() は正の値を返します。

cv_timedwait(9F) には、システムが最後にリブートしてからのクロック刻みで表した絶対待ち時間を指定する必要があります。待ち時間は、ddi_get_lbolt(9F) で現在の値を取得することによって調べることができます。ドライバは通常、最大待ち時間の値を秒またはマイクロ秒単位で保持しているため、drv_usectohz(9F) によってこの値がクロック刻みに変換され、ddi_get_lbolt(9F) で得られた値に加算されます。

次の例は、呼び出し元に EIO を返す前に、cv_timedwait(9F) を使用してデバイスアクセスを最大 5 秒待機する方法を示しています。

使用例 2  cv_timedwait() の使用
clock_t            cur_ticks, to;
mutex_enter(&xsp->mu);
while (xsp->busy) {
        cur_ticks = ddi_get_lbolt();
        to = cur_ticks + drv_usectohz(5000000); /* 5 seconds from now */
        if (cv_timedwait(&xsp->cv, &xsp->mu, to) == -1) {
                /*
                 * The timeout time 'to' was reached without the
                 * condition being signaled.
                 */
                /* tidy up and exit */
                mutex_exit(&xsp->mu);
                return (EIO);
        }
}
xsp->busy = 1;
mutex_exit(&xsp->mu);

デバイスドライバの書き込み処理では通常、cv_timedwait(9F) よりも cv_wait(9F) を優先的に使用しますが、cv_wait(9F) を使用するほうが望ましい場合があります。たとえば、ドライバが次のような条件で待機している場合は、cv_wait(9F) のほうが適しています。

  • 内部ドライバ状態の変更。このような状態変更が発生するには、何らかのコマンドの実行または一定時間の経過が必要な場合があります。

  • ドライバでシングルスレッド処理する必要がある何らかの要素

  • タイムアウトの可能性をすでに管理している何らかの状況。「A」が「B」に依存しており、「B」が cv_timedwait(9F) を使用している場合などです。

cv_wait_sig() 関数

発生する可能性がない条件や、長期間にわたって発生しない条件をドライバが待機している場合があります。そのような場合、ユーザーがシグナルを送信してスレッドを終了させることができます。ドライバの設計によっては、シグナルを送信してもドライバが復帰しない可能性があります。

cv_wait_sig(9F) を使用すると、シグナルによってスレッドのブロックを解除できます。ユーザーは kill(1) を使用してシグナルをスレッドに送信するか、または割り込み文字を入力することによって、長期に及ぶ可能性がある待機を解除できます。cv_wait_sig(9F) は、シグナルを送信してから戻る場合は 0 を返し、条件が発生した場合は 0 以外を返します。ただし、シグナルを受信できないケースも存在します。スレッドがシグナルを受信できないを参照してください。

次の例は、cv_wait_sig(9F) を使用してシグナルを送信し、スレッドのブロックを解除する方法を示しています。

使用例 3  cv_wait_sig() の使用
mutex_enter(&xsp->mu);
while (xsp->busy) {
        if (cv_wait_sig(&xsp->cv, &xsp->mu) == 0) {
        /* Signaled while waiting for the condition */
                /* tidy up and exit */
                mutex_exit(&xsp->mu);
                return (EINTR);
        }
}
xsp->busy = 1;
mutex_exit(&xsp->mu);

cv_timedwait_sig() 関数

cv_timedwait_sig(9F)cv_timedwait(9F) および cv_wait_sig(9F) に似ています。異なる点として、cv_timedwait_sig() はタイムアウト到達後、条件のシグナルを送信せずに -1 を返します。また、kill(2) などのシグナルがスレッドに送信された場合は 0 を返します。

cv_timedwait(9F)cv_timedwait_sig(9F) の両方で、時間は最後にシステムがリブートしてからの絶対クロック刻みで計測されます。