Oplock の破損
便宜的ロックを要求して許可されると、その便宜的ロックの所有者は、要求した便宜的ロックの種類に基づいてストリームにアクセスできます。 受信した操作が現在の oplock と互換性がない場合、システムは oplock を中断します。
oplock が付与されると、システムは要求する IRP をペンドします。 便宜的ロックが解除されると、保留されていた便宜的ロックの要求 IRP は STATUS_SUCCESS で完了します。 Level 1、Batch、Filter の各便宜的ロックでは、IRP の IoStatus.Information メンバーが、便宜的ロックの解除後のレベルを示すように設定されます。 これらのレベルは次のとおりです。
FILE_OPLOCK_BROKEN_TO_NONE: 便宜的ロックは解除され、現在ストリームに便宜的ロックはありません。 便宜的ロックは「None に解除されている」と表示されています。
FILE_OPLOCK_BROKEN_TO_LEVEL_2: 現在の便宜的ロック (Level 1 または Batch) は、Level 2 便宜的ロックに変換されました。 フィルター オプロックはレベル 2 にダウングレードすることは決してなく、常に「なし」にダウングレードします。
Read-Handle、Read-Write、Read-Write-Handle の各便宜的ロックの場合、便宜的ロックの解除後のレベルは、DeviceIoControl の lpOutBuffer パラメーターとして渡される REQUEST_OPLOCK_OUTPUT_BUFFER 構造体の NewOplockLevel メンバーのフラグ OPLOCK_LEVEL_CACHE_READ、OPLOCK_LEVEL_CACHE_HANDLE、または OPLOCK_LEVEL_CACHE_WRITE の 0 個以上の組み合わせとして記述されます。 同様に、FltFsControlFile と ZwFsControlFile を使用して、カーネルモードから Windows 7 のオプロックを要求することができます。 詳細については、FSCTL_REQUEST_OPLOCKを参照してください。
システムの便宜的ロック パッケージが、Level 1、Batch、Filter、Read-Write、Read-Write-Handle、または Read-Handle (特定の状況下) の各便宜的ロックを解除する場合:
- 便宜的ロックパッケージは、保留されている便宜的ロック要求 IRP を完了します。
- 便宜的ロックの解除の原因になった操作は、それ自体が保留されます。
I/O マネージャーは、操作が次の場合に、STATUS_PENDINGを返すのではなく、操作をブロックします。
- 同期ハンドルで発行されます。
- 常に同期される IRP_MJ_CREATE です。
I/O マネージャーは、便宜的ロックの所有者から、便宜的ロック パッケージの処理が完了し、保留中の操作が安全に続行できることを通知する確認応答を待機します。 この遅延により、oplock 所有者は、現在の操作が続行される前にストリームを一貫した状態に戻すことができます。 タイムアウトがないため、システムは受信確認の受信を永久に待機します。 したがって、オプロックの所有者は、タイムリーに休憩を認める必要があります。 保留されている操作の IRP はキャンセル可能な状態に設定されます。 待機を実行しているアプリケーションまたはドライバーが終了すると、oplock パッケージは直ちにSTATUS_CANCELLEDで IRP を完了します。
IRP_MJ_CREATE IRP では、便宜的ロック解除確認応答の一環としてブロックされないよう、FILE_COMPLETE_IF_OPLOCKED 作成オプションを指定できます。 このオプションは、oplock ブレーク受信確認が受信されるまで、作成 IRP をブロックしないように oplock パッケージに指示します。 代わりに、作成の続行が許可されます。 作成が成功しても oplock の解除が発生した場合、戻りコードは STATUS_SUCCESS ではなく STATUS_OPLOCK_BREAK_IN_PROGRESS になります。 通常、FILE_COMPLETE_IF_OPLOCKED フラグはデッドロックを回避するために使用されます。 たとえば、ストリームに対する便宜的ロックを所有しているクライアントが、後で同じストリームを開いた場合、そのクライアントは自分自身が便宜的ロック解除の受信確認を行うのを待って、ブロック状態になります。 このシナリオでは、FILE_COMPLETE_IF_OPLOCKED フラグを使用するとデッドロックが回避されます。
NTFS ファイルシステムは、共有違反をチェックする前に、バッチとフィルターのオプロックの中断を開始します。 そのため、FILE_COMPLETE_IF_OPLOCKED を指定して作成した操作が STATUS_SHARING_VIOLATION で失敗することがあっても、バッチまたはフィルターの便宜的ロックが解除される可能性があります。 この場合、IO_STATUS_BLOCK 構造体の情報メンバーは、呼び出し元がこのケースを検出できるようにFILE_OPBATCH_BREAK_UNDERWAYに設定されます。
Read-Handle と Read-Write-Handle 便宜的ロックの場合、便宜的ロック解除は NTFS のチェックの後で開始され、共有違反を検出します。 このシーケンスによって、oplock 所有者がハンドルを閉じて干渉を避ける機会が提供されるため、ユーザーに共有違反を通知しない可能性が生じます。 また、oplock がキャッシュしているハンドルが新しい作成と競合しない場合には、oplock を無条件に解除することを防ぎます。
Level 2、Read、および Read-Handle (特定の状況下) の各便宜的ロックが解除されるとき、システムは受信確認を待機しません。 その理由は、他のクライアントにアクセスを許可する前に、ファイルに復元する必要があるキャッシュされた状態がストリームに存在しないことです。
現在の oplock 状態をチェックして、oplock を壊す必要があるかどうかを判断する特定のファイル システム操作があります。 次の操作固有の記事では、oplock の中断をトリガーする原因、oplock が中断するレベルを決定する内容、および中断の受信確認が必要かどうかを説明します。
- IRP_MJ_CREATE
- IRP_MJ_READ
- IRP_MJ_WRITE
- IRP_MJ_CLEANUP
- IRP_MJ_LOCK_CONTROL
- IRP_MJ_SET_INFORMATION
- IRP_MJ_FILE_SYSTEM_CONTROL
- FS_FILTER_ACQUIRE_FOR_SECTION_SYNCHRONIZATION
Windows 7 の便宜的ロックの解除では、DeviceIoControl(lpOutBuffer)、FltFsControlFile(OutBuffer)、または ZwFsControlFile(OutBuffer) の出力パラメーターとして渡される REQUEST_OPLOCK_OUTPUT_BUFFER 構造体の Flags に REQUEST_OPLOCK_OUTPUT_FLAG_ACK_REQUIRED フラグが設定されている場合、受信応答が必要です。 詳細については、FSCTL_REQUEST_OPLOCKを参照してください。
一覧の操作ごとの記事では、Read-Handle 便宜的ロックの解除によって、便宜的ロックを解除した操作が保留される場合の詳細が説明されています。 たとえば、IRP_MJ_CREATE 記事には、関連付けられている Read-Handle の詳細が含まれています。