Solaris 動的トレースガイド

第 18 章 lockstat プロバイダ

lockstat プロバイダは、ロック競合の統計情報を調べたり、ロックの動作をあらゆる面から理解するために役立つ各種プローブを提供します。lockstat(1M) コマンドは、lockstat プロバイダを使って生のデータを収集する DTrace コンシューマです。

概要

lockstat プロバイダが提供するプローブは、競合イベントプローブと保持イベントプローブの 2 種類です。

競合イベント」プローブは、同期プリミティブの競合に関するプローブです。このプローブは、スレッドが利用可能なリソースを待機する必要が生じた時点で起動します。一般に Solaris は、競合が起こらないように最適化されているため、競合状態が長時間続くことは考えられません。これらの競合イベントプローブは、実際に競合が発生した場合、その状況を把握するために使用します。競合はめったに発生しないので、競合イベントプローブを有効にしても、通常、パフォーマンスにはさほど影響はありません。

保持イベント」プローブは、同期プリミティブの獲得や解放などの操作に関するプローブです。保持イベントプローブは、同期プリミティブの操作方法に関するさまざまな問題に答えを出すために使用します。Solaris は、同期プリミティブの獲得/解放を非常に頻繁に行います。その回数は、ビジー状態のシステムで、CPU 当たり毎秒数 100 万回にも及びます。このため、競合イベントプローブより保持イベントプローブを有効にしたときのほうが、プローブの及ぼす影響ははるかに大きくなります。プローブを有効にしたときの影響は甚大になりえますが、決して異常なものではありません。本稼働システムでプローブを有効にしても、まったく問題はありません。

lockstat プロバイダは、Solaris のさまざまな同期プリミティブに相当するプローブを提供します。ここからは、これらのプリミティブとプローブについて説明します。

適応型ロックプローブ

適応型ロック」は、クリティカルセクションに対する相互排他ロックです。このロックは、ほとんどのカーネルコンテキストで獲得可能です。また、コンテキスト制限がほとんどないので、Solaris カーネル内の同期プリミティブの大部分に対応しています。これらのロックは、競合に対する動作に適応性があります。 スレッドは、獲得しようとする適応型ロックがすでに所有されているとき、ロックを所有しているスレッド (所有スレッド) が CPU 上で実行中であるかどうかを確認します。所有スレッドが別の CPU 上で実行中であれば、このスレッド (ロックを獲得しようとしているスレッド) は「スピン」します。所有スレッドが実行中でなければ、「ブロック」されます。

適応型ロック関連の lockstat プローブは、表 18–1 に示すとおり 4 種類あります。これらの各プローブの arg0 には、適応型ロックに相当する kmutex_t 構造体へのポインタが格納されます。

表 18–1 適応型ロックプローブ

adaptive-acquire

適応型ロックの獲得後すぐに起動する保持イベントプローブ。 

adaptive-block

保持されている適応型相互排他ロックでブロックされたスレッドが再起動し、この相互排他ロックを獲得したあと起動する競合イベントプローブ。adaptive-acquireadaptive-block の両方が有効なときは、adaptive-block のほうが先に起動します。1 回のロック獲得で adaptive-blockadaptive-spin の両方のプローブが起動することもあります。adaptive-blockarg1 には、スリープ時間 (ナノ秒単位) が入ります。

adaptive-spin

保持されている適応型相互排他ロックでスピンしたスレッドが、この相互排他ロックを正常に獲得したあと起動する競合イベントプローブ。adaptive-acquireadaptive-spin の両方が有効なときは、adaptive-spin のほうが先に起動します。1 回のロック獲得で adaptive-blockadaptive-spin の両方のプローブが起動することもあります。adaptive-spinarg1 には、「スピン時間」が入ります。スピン時間とは、ロックが獲得されるまでスピンループで消費されたナノ秒数を示す値です。

adaptive-release

適応型ロックの解放後すぐに起動する保持イベントプローブ。 

スピンロックプローブ

カーネル内の一部のコンテキストでは、スレッドをブロックできません。該当するコンテキストとして、ハイレベル割り込みコンテキストや、ディスパッチャの状態を操作するコンテキストがあります。これらのコンテキストでは、スレッドをブロックできないため、適応型ロックを使用できません。これらのコンテキストでクリティカルセクションに対する相互排他を実現するには、適応型ロックの代わりに「スピンロック」を使用します。スピンロックは、その名前からわかるように、競合が発生すると、所有スレッドがロックを解放するまで「スピン」します。スピンロック関連の 3 つのプローブについては、表 18–2 のとおりです。

表 18–2 スピンロックプローブ

spin-acquire

スピンロックの獲得後すぐに起動する保持イベントプローブ。 

spin-spin

保持されているスピンロックでスピンしたスレッドが、このスピンロックを正常に獲得したあと起動する競合イベントプローブ。spin-acquirespin-spin の両方が有効なときは、spin-spin のほうが先に起動します。spin-spin arg1 には、「スピン時間」が入ります。スピン時間とは、ロックが獲得されるまでスピン状態で消費されたナノ秒数を示す値です。スピンカウントは、スピン時間の比較に利用されます。単独ではほとんど利用されません。

spin-release

スピンロックの解放後すぐに起動する保持イベントプローブ。 

スピンロックよりも適応型ロックのほうが一般的です。以下のスクリプトで、2 種類のロックのそれぞれの合計を表示すると、この観察が裏付けられます。

lockstat:::adaptive-acquire
/execname == "date"/
{
	@locks["adaptive"] = count();
}

lockstat:::spin-acquire
/execname == "date"/
{
	@locks["spin"] = count();
}

このスクリプトの実行を開始したら、別のウィンドウを開いて date(1) コマンドを実行します。DTrace スクリプトを終了すると、次のような出力が得られます。


# dtrace -s ./whatlock.d
dtrace: script './whatlock.d' matched 5 probes 
^C
spin                                                             26
adaptive                                                       2981

この出力からわかるように、date コマンドの実行中に獲得されたロックの 99 パーセントは適応型ロックです。date コマンドのような単純な処理でも、このように多くのロックが獲得されます。Solaris カーネルに代表されるきわめてスケーラブルなシステムでは、きめ細かいロック処理が要求されます。たくさんのロックを獲得する必要があるのは、このためです。

スレッドロック

スレッドロック」は、スレッド状態を変更する目的でスレッドをロックするために使用する、特殊なスピンロックです。スレッドロック保持イベントは、スピンロック保持イベントプローブ (spin-acquirespin-release) を使用します。これに対して、競合イベントは、スレッドロック固有のプローブを独自に備えています。スレッドロック保持イベントプローブを、表 18–3 に示します。

表 18–3 スレッドロックプローブ

thread-spin

スレッドロックに対してスレッドがスピンしたあと起動する競合イベントプローブ。その他の競合イベントプローブと同じように、競合イベントプローブと保持イベントプローブの両方が有効になっている場合、thread-spinspin-acquire より先に起動します。thread-spin は、実際にロックが獲得される前に起動します。この点は、その他の競合イベントプローブとは異なります。その結果、何回かの thread-spin プローブの起動は、1 回の spin-acquire プローブの起動と同じことになります。

読み取り/書き込みロックプローブ

「読み取り/書き込みロック」は、クリティカルセクション内で、「複数の読み取り」と「単一の書き込み」のいずれか一方を許可します。通常、これらのロックを使用するのは、変更されるより検索される機会のほうが多く、かつクリティカルセクション時間が十分にある構造体です。クリティカルセクション時間が短い場合、読み取り/書き込みロックは、ロックを実装するために使用された共有メモリー上で暗黙的に直列化されます。この場合、読み取り/書き込みロックは、適応型ロックと同じになります。読み取り/書き込みロックの詳細については、rwlock(9F) のマニュアルページを参照してください。

読み取り/書き込みロック関連のプローブは、表 18–4 のとおりです。これらの各プローブの arg0 には、適応型ロックに相当する krwlock_t 構造体へのポインタが格納されます。

表 18–4 読み取り/書き込みロックプローブ

rw-acquire

読み取り/書き込みロックの獲得後すぐに起動する保持イベントプローブ。arg1 には、定数 RW_READER (ロックが読み取りとして獲得された場合) または RW_WRITER (ロックが書き込みとして獲得された場合) が入ります。

rw-block

保持されている読み取り/書き込みロックでブロックされたスレッドが再起動し、このロックを獲得したあと起動する競合イベントプローブ。arg1 には、現在のスレッドがロックを獲得するまでのスリープ時間 (ナノ秒) が入ります。arg2 には、定数 RW_READER (ロックが読み取りとして獲得された場合) または RW_WRITER (ロックが書き込みとして獲得された場合) が入ります。arg3arg4 には、ブロックの原因に関する情報が入ります。現在のスレッドがブロックされたとき保持されていたロックが「書き込み」だった場合にかぎり、arg3 にはゼロ以外の値が入ります。arg4 には、現在のスレッドがブロックされたときの読み取りカウントが入ります。rw-blockrw-acquire の両方のプローブが有効になっている場合、rw-block のほうが rw-acquire より先に起動します。

rw-upgrade

スレッドが、読み取り/書き込みロックを、読み取り側から書き込み側へ正常に昇格させたあと起動する保持イベント。昇格は、ブロックしないインタフェース rw_tryupgrade(9F) でしか行うことができません。このため、昇格には競合イベントがありません。

rw-downgrade

スレッドが、読み取り/書き込みロックの所有権を、書き込み側から読み取り側へ降格したあと起動する保持イベント。降格は、常に競合なしで正常に行われます。このため、降格には競合イベントがありません。 

rw-release

読み取り/書き込みロックの解放後すぐに起動する保持イベントプローブ。arg1 には、定数 RW_READER (解放されたロックが読み取りとして保持されていた場合) または RW_WRITER (解放されたロックが書き込みとして保持されていた場合) が入ります。昇格と降格のため、ロックを獲得したとき、まだこのロックが解放されていない場合もあります。

安定性

以下の表に、lockstat プロバイダの安定性を DTrace の安定性機構に従って示します。安定性機構の詳細については、第 39 章安定性を参照してください。

要素 

名前の安定性 

データの安定性 

依存クラス 

プロバイダ 

発展中 

発展中 

共通

モジュール 

非公開 

非公開 

不明 

機能 

非公開 

非公開 

不明 

名前 

発展中 

発展中 

共通

引数 

発展中 

発展中 

共通