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

印刷ビューの終了

更新: 2016 年 11 月
 
 

ロックプリミティブ

従来の UNIX システムでは、カーネルコードのすべてのセクションは、sleep(1) の明示的呼び出しによりプロセッサを解放して終了するか、またはハードウェア割り込みによって終了します。Oracle Solaris OS の動作は異なります。別のスレッドを実行するためにカーネルスレッドをいつでも横取りできます。すべてのカーネルスレッドはカーネルアドレス空間を共有し、同じデータの読み取りおよび変更を行う必要がしばしば生じるため、スレッドが共有データを破棄することを防ぐために、カーネルは多くのロックプリミティブを提供します。これらのメカニズムには、(mutex とも呼ばれる) 相互排他ロック、読み取り/書き込みロック、およびセマフォーが含まれます。

ドライバデータのストレージクラス

データアクセスを制御するための明示的手順をドライバで実行する必要があるかどうかは、データのストレージクラスによって決まります。データストレージクラスには次の 3 種類があります。

  • 自動 (スタック) データ。すべてのスレッドはプライベートスタックを備えているため、ドライバで自動変数のロックが必要になることはありません。

  • グローバル静的データ。グローバル静的データは、ドライバ内の任意の数のスレッドによって共有できます。ドライバでこの種類のデータをロックすることが必要になる場合があります。

  • カーネルヒープデータkmem_alloc(9F) によって割り当てられるデータなど、カーネルヒープデータは、ドライバ内の任意の数のスレッドによって共有できます。ドライバはこのような共有データを常に保護する必要があります。

相互排他ロック

相互排他ロック (mutex) は通常、データの集合と関連付けられ、そのデータへのアクセスを制御します。mutex は、ある時点で 1 つのスレッドのみがそのデータにアクセスできるようにする手段を提供します。mutex 関数には次のものがあります。

mutex_destroy(9F)

関連付けられたストレージを解放します。

mutex_enter(9F)

mutex を取得します。

mutex_exit(9F)

mutex を解放します。

mutex_init(9F)

mutex を初期化します。

mutex_owned(9F)

mutex が現在のスレッドによって保持されているかどうかを評価します。ASSERT(9F) でのみ使用します。

mutex_tryenter(9F)

利用可能な場合に mutex を取得しますが、ブロックはしません。

mutex の設定

デバイスドライバは通常、ドライバデータ構造ごとに mutex を割り当てます。一般に mutex は、kmutex_t 型の構造体のフィールドです。使用する mutex を準備するために mutex_init(9F) が呼び出されます。この呼び出しは通常、デバイスごとの mutex については attach(9E) の時点で、グローバルドライバ mutex については _init(9E) の時点で行われます。

次に例を示します。

struct xxstate *xsp;
/* ... */
mutex_init(&xsp->mu, NULL, MUTEX_DRIVER, NULL);
/* ... */

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

ドライバはアンロードされる前に mutex_destroy(9F) を使用して mutex を破棄する必要があります。mutex の破棄は通常、デバイスごとの mutex については detach(9E) の時点で、グローバルドライバ mutex については _fini(9E) の時点で行われます。

mutex の使用

共有データ構造を読み書きする必要があるドライバコードのすべてのセクションで、次のタスクを実行する必要があります。

  • mutex の取得

  • データへのアクセス

  • mutex の解放

mutex のスコープ、つまり mutex が保護するデータはすべてプログラマが管理します。mutex によってデータ構造が保護されるのは、データ構造にアクセスするすべてのコードパスが、mutex の保持中にデータにアクセスする場合に限られます。

読み取り/書き込みロック

読み取り/書き込みロックはデータセットへのアクセスを制御します。読み取り/書き込みロックに関しては、多数のスレッドが読み取りのためにロックを同時に保持できる一方で、書き込みのためにロックを保持できるのは 1 つのスレッドに限られます。

ほとんどのデバイスドライバは読み取り/書き込みロックを使用しません。これらのロックは mutex よりも低速です。これらのロックがパフォーマンス面で優位になるのは、読み取りの割合が多く、書き込みの頻度の低いデータを保護するときのみです。このようなケースでは、mutex の競合がボトルネックになる可能性があるため、読み取り/書き込みロックを使用したほうが効率的な場合があります。読み取り/書き込みロック関数を次の表にまとめています。詳細は、rwlock(9F) のマニュアルページを参照してください。読み取り/書き込みロック関数には次のものがあります。

rw_destroy(9F)

読み取り/書き込みロックを破棄します。

rw_downgrade(9F)

読み取り/書き込みロックを保持するスレッドを書き込みから読み取りに降格します。

rw_enter(9F)

読み取り/書き込みロックを取得します。

rw_exit(9F)

読み取り/書き込みロックを解放します。

rw_init(9F)

読み取り/書き込みロックを初期化します

rw_read_locked(9F)

読み取り/書き込みロックが、読み取りと書き込みのどちらの目的で保持されているかを調べます。

rw_tryenter(9F)

待機することなく、読み取り/書き込みロックの取得を試みます。

rw_tryupgrade(9F)

読み取り/書き込みロックを読み取りから書き込みに昇格しようと試みます。

セマフォー

カウントセマフォーは、デバイスドライバ内部でスレッドを管理するための代替のプリミティブとして使用できます。詳細については、semaphore(9F) のマニュアルページを参照してください。セマフォー関数には次のものがあります。

sema_destroy(9F)

セマフォーを破棄します。

sema_init(9F)

セマフォーを初期化します。

sema_p(9F)

セマフォーを 1 減らします。ブロックする可能性があります。

sema_p_sig(9F)

セマフォーを 1 減らしますが、シグナルが保留中の場合にはブロックしません。スレッドがシグナルを受信できないを参照してください。

sema_tryp(9F)

セマフォーを 1 減らそうと試みますが、ブロックしません。

sema_v(9F)

セマフォーを 1 増やします。待機中スレッドをブロック解除する可能性があります。