ランダウン防止

Windows XP 以降、カーネル モード ドライバーでランダウン保護を使用できます。 ドライバーはランダウン保護を使用して、別のカーネル モード ドライバーによって作成・削除された共有システム メモリ内のオブジェクトに安全にアクセスできます。

オブジェクトのすべての未処理のアクセスが完了し、オブジェクトにアクセスするために許可される新しい要求がない場合、オブジェクトはランダウンしたことになります。 たとえば、共有オブジェクトを削除して新しいオブジェクトに置き換えるために、共有オブジェクトをランダウンすることが必要になる場合です。

共有オブジェクトを所有するドライバーは、他のドライバーがオブジェクトのランダウン保護を取得して解放できるようにします。 ランダウン保護が有効な場合、所有者以外のドライバーは、アクセスが完了する前に所有者がオブジェクトを削除してしまうリスクなしでオブジェクトにアクセスできます。 アクセスが開始する前に、アクセスするドライバーはオブジェクトのランダウン保護を要求します。 有効期間の長いオブジェクトの場合、この要求はほぼ常に許可されます。 アクセスが完了すると、アクセスしているドライバーは、オブジェクトに対して以前に取得したランダウン保護を解放します。

プライマリ ランダウン保護ルーチン

オブジェクトの共有を開始するために、オブジェクトを所有するドライバーは ExInitializeRundownProtection ルーチンを呼び出して、オブジェクトのランダウン保護を初期化します。 この呼び出し後、オブジェクトにアクセスする他のドライバーは、オブジェクトのランダウン保護を取得して解放できます。

共有オブジェクトにアクセスするドライバーは、ExAcquireRundownProtection ルーチンを呼び出して、オブジェクトのランダウン保護を要求します。 アクセスが完了すると、このドライバーは ExReleaseRundownProtection ルーチンを呼び出して、オブジェクトのランダウン保護を解放します。

所有ドライバーが共有オブジェクトを削除する必要があると判断する場合、このドライバーは、オブジェクトのすべての未処理のアクセスが完了するまでオブジェクトの削除を待機します。

共有オブジェクトを削除する準備として、所有ドライバーは ExWaitForRundownProtectionRelease ルーチンを呼び出し、オブジェクトのランダウンを待ちます。 この呼び出し中、ExWaitForRundownProtectionRelease は、オブジェクトに対して以前に許可されたすべてのランダウン保護インスタンスが解放されるまで待ちますが、オブジェクトに対するランダウン保護の新しい要求が許可されないようにします。 最後に保護されたアクセスが完了し、ランダウン保護のすべてのインスタンスが解放されると、ExWaitForRundownProtectionRelease が返され、所有ドライバーはオブジェクトを安全に削除できます。

ExWaitForRundownProtectionRelease は、共有オブジェクトのランダウン保護を保持しているすべてのドライバーがこの保護を解放するまで、呼び出し元のドライバー スレッドの実行をブロックします。 ExWaitForRundownProtectionRelease が過度な長期間にわたって実行をブロックしないようにするには、オブジェクトに対するランダウン保護を保持している間、共有オブジェクトにアクセスするドライバー スレッドが中断されないようにする必要があります。 このため、アクセスしているドライバーは、重要なリージョンまたは保護されたリージョン内で、または IRQL = APC_LEVEL で実行中に ExAcquireRundownProtectionExReleaseRundownProtection を呼び出す必要があります。

ランダウン保護の使用

ランダウン保護は、ほぼ常に使用可能でありながら、時には削除して置き換える必要があるような共有オブジェクトへのアクセスを提供する場合に特に便利です。 データにアクセスするドライバー、またはこのオブジェクトのルーチンを呼び出すドライバーは、削除後にオブジェクトへのアクセスを試みるべきではありません。 そうしないと、これらの無効なアクセスによって、予期しない動作、データの破損、さらにはシステム障害が発生することがあります。

たとえば、ウイルス対策ドライバーは通常、オペレーティング システムの実行中にメモリに読み込まれたままになります。 場合によっては、このドライバーをアンロードし、ドライバーの更新されたリリース版に置き換えることが必要になります。 他のドライバーは、このドライバーのデータとルーチンにアクセスするウイルス対策ドライバーに I/O 要求を送信します。 I/O 要求を送信する前に、ファイル システム フィルター マネージャーなどのカーネル コンポーネントは、I/O 要求の処理中にウイルス対策ドライバーの尚早なアンロードを防ぐためにランダウン保護を取得できます。 I/O 要求が完了したら、ランダウン保護を解放できます。

ランダウン保護は、共有オブジェクトへのアクセスをシリアル化しません。 2 つ以上のアクセス ドライバーがオブジェクトのランダウン保護を同時に保持できます。オブジェクトへのアクセスをシリアル化する必要がある場合は、相互排他ロックなどの他のメカニズムを使用してアクセスをシリアル化する必要があります。

EX_RUNDOWN_REF 構造体

EX_RUNDOWN_REF 構造体は、共有オブジェクトのランダウン保護の状態を追跡します。 この構造体はドライバーに対して不透明です。 システムが提供するランダウン保護ルーチンは、この構造体を使用して、オブジェクトに現在有効なランダウン保護のインスタンスの数をカウントします。 これらのルーチンは、この構造体を使用して、オブジェクトがランダウンしているか、またはランダウンのプロセス中であるかを追跡します。

オブジェクトの共有を開始するには、オブジェクトを所有するドライバーが ExInitializeRundownProtection を呼び出して、オブジェクトに関連付けられている EX_RUNDOWN_REF 構造体を初期化します。 初期化後、所有ドライバーは、オブジェクトへのアクセスを必要とする他のドライバーがこの構造体を使用できるようにします。 アクセスするドライバーは、オブジェクトのランダウン保護を取得して解放する ExAcquireRundownProtection 呼び出しと ExReleaseRundownProtection 呼び出しにパラメーターとしてこの構造体を渡します。 所有ドライバーは、オブジェクトがランダウンするまで待機する ExWaitForRundownProtectionRelease 呼び出しへのパラメータとしてこの構造体を渡し、安全に削除されるようにします。

ロックとの比較

ランダウン保護は、共有オブジェクトへの安全なアクセスを保証するいくつかの方法の 1 つです。 もう 1 つには、相互除外ソフトウェア ロックを使用する方法があります。 ドライバーが別のドライバーによって現在ロックされているオブジェクトへのアクセスを必要とする場合、最初のドライバーは、2 番目のドライバーがロックを解除するまで待機する必要があります。 ただし、ロックの取得と解放はパフォーマンスの妨げになり、またロックは大量のメモリを消費することがあります。 ロックの誤った使用により、同じ共有オブジェクトに対して競合するドライバーがデッドロックになる可能性があります。 デッドロックを検出して回避するには、通常、大量のコンピューティング リソースを分散することが必要になります。

ロックとは対照的に、ランダウン保護では比較的軽量な処理時間とメモリ要件があります。 オブジェクトへの未処理のアクセスがすべて完了するまでオブジェクトの削除を遅延させるためのシンプルなリファレンス カウントがオブジェクトに関連付けられています。 この方法では、相互除外ソフトウェア ロックの代わりにアトミックなインターロック ハードウェア命令を使用して、オブジェクトへの安全なアクセスを保証できます。 ランダウン保護の取得と解放の呼び出しは、通常の場合、とても高速です。 ランダウン保護などの軽量メカニズムを使用する利点は、多くのドライバー間で共有され、長い寿命の共有オブジェクトにとっては重要になることがあります。

その他のランダウン保護ルーチン

これまでに取り上げたものに加えて、他のいくつかのランダウン保護ルーチンを使用できます。 これらの追加ルーチンは、一部のドライバーで使用できることがあります。

ExReInitializeRundownProtection ルーチンを使用すると、以前に使用した EX_RUNDOWN_REF 構造体を新しいオブジェクトに関連付けて、このオブジェクトのランダウン保護を初期化できます。

ExRundownCompleted ルーチンは、関連付けられたオブジェクトのランダウンが完了したことを示すために EX_RUNDOWN_REF 構造体を更新します。

ExAcquireRundownProtectionEx ルーチンと ExReleaseRundownProtectionEx ルーチンは、ExAcquireRundownProtection および ExReleaseRundownProtection に類似しています。 これら 4 つのルーチンは、共有オブジェクトに対して有効なランダウン保護のインスタンスの数をインクリメントまたはデクリメントします。 一方で、ExAcquireRundownProtectionExReleaseRundownProtection は、このカウントを 1 ずつインクリメントおよびデクリメントします。ExAcquireRundownProtectionExExReleaseRundownProtectionEx は、カウントを任意の数だけインクリメントおよびデクリメントします。

キャッシュ対応のランダウン保護

ランダウン リファレンスはコンパクトで高速なデータ構造体ですが、多くのプロセッサが同時にリファレンスを取得しようとするとキャッシュの競合が発生することがあります。 これは、ドライバーのパフォーマンスとスケーラビリティに影響する可能性があります。

この問題を回避するには、キャッシュ対応のランダウン リファレンスを使用して、リファレンス追跡を複数のキャッシュ ラインに分散できます。 これにより、キャッシュの競合が軽減し、マルチプロセッサ コンピューターにおけるドライバーのパフォーマンスが向上します。

キャッシュ対応のランダウン リファレンスを使用するには、次の手順を実施します。

  1. 次のいずれかの操作を行って、EX_RUNDOWN_REF_CACHE_AWARE オブジェクトを作成します。
  2. ExAcquireRundownProtectionCacheAware ルーチンを呼び出して、オブジェクトにアクセスする前に、オブジェクトのランダウン保護を要求します。 このルーチンは、要求が許可されている場合には TRUE を返し、オブジェクトが実行されている場合には FALSE を返します。
  3. ExReleaseRundownProtectionCacheAware ルーチンを呼び出して、オブジェクトへのアクセス後にオブジェクトのランダウン保護を解放します。
  4. ExWaitForRundownProtectionReleaseCacheAware ルーチンを呼び出して、オブジェクトを削除する前に、オブジェクトがランダウンするまで待機します。 このルーチンは、オブジェクトのランダウン保護のすべてのインスタンスが解放されるまで、現在のスレッドをブロックします。
  5. 前段階で ExAllocateCacheAwareRundownProtection というドライバーを呼び出していた場合は、ExFreeCacheAwareRundownProtection を呼び出してランダウン リファレンスを解放する必要があります。

キャッシュ対応のランダウン リファレンスを再度使用するには、次の手順を実施します。

  1. ExWaitForRundownProtectionReleaseCacheAware を呼び出してから、ExRundownCompletedCacheAware を呼び出して古いオブジェクトのランダウンが完了したことを示します。
  2. ExReInitializeRundownProtectionCacheAware を呼び出して、関連付けられているオブジェクトがランダウンした後にリファレンスを再初期化します。
  3. これで、ドライバーは ExAcquireRundownProtectionCacheAware を再度呼び出すことができます。

キャッシュ対応のランダウン リファレンスには、特定の状況でパフォーマンスとスケーラビリティが向上する利点がありますが、通常のランダウン リファレンスよりも多くのメモリを消費します。 2 種類のランダウン リファレンスから選択するような場合に、このトレードオフを検討してください。