次の方法で共有


oplock の要求と許可

ネットワーク リダイレクターは、リモート サーバー上のファイルにアクセスすると、リモート サーバーから oplock を要求します。 クライアント アプリケーションは、ロックがローカル サーバー上のファイルを対象としている場合にのみ、oplock を直接要求します。

OplocksFSCTLs.を通じてリクエストされます。 次の FSCTLは、ユーザーモードアプリケーションとカーネルモードドライバーの両方から発行できる、さまざまな 型のオプトロックに使用されます。

ユーザーモードでのオプロックの要求

ユーザーモードで Windows 7 の oplock を要求するには、DeviceIoControlを呼び出します。

  • dwIoControlCodeFSCTL_REQUEST_OPLOCK に設定します。
  • lpInBuffer パラメーターで REQUEST_OPLOCK_INPUT_BUFFER 構造体へのポインターを渡します。
    • oplock 要求の書式を設定する方法については、その構造体のドキュメントを参照してください。
  • lpOutBuffer パラメーターで REQUEST_OPLOCK_OUTPUT_BUFFER 構造体へのポインターを渡します。

詳細については、FSCTL_REQUEST_OPLOCK を参照してください。

要求された oplock を許可できる場合、DeviceIoControl は FALSE を返し、GetLastErrorは ERROR_IO_PENDING を返します。 このため、同期 I/O に対して oplock が付与されることはありません。 重複する操作は、oplock が壊れるまで完了しません。 操作が完了すると、REQUEST_OPLOCK_OUTPUT_BUFFER には oplock の解除に関する情報が含まれます。

oplock を許可できない場合、ファイル システムは適切なエラー コードを返します。 最も一般的に返されるエラー コードは、ERROR_OPLOCK_NOT_GRANTED と ERROR_INVALID_PARAMETER です。

カーネル モードでの oplock の要求

カーネルモードでWindows 7のオプノロックを要求するには、

ファイル システム ミニフィルター

ファイル システムミニフィルターでは、FltAllocateCallbackData を使用して、次のように割り当てられた FLT_CALLBACK_DATA を入力する必要があります。

  • Iopb->MajorFunction フィールドを IRP_MJ_FILE_SYSTEM_CONTROL に設定します。
  • Iopb->MinorFunction フィールドを IRP_MN_USER_FS_REQUEST に設定します。
  • Iopb->Parameters.FileSystemControl.Buffered.FsControlCode メンバーを FSCTL_REQUEST_OPLOCK に設定します。
  • サイズが大きい REQUEST_OPLOCK_INPUT_BUFFER または REQUEST_OPLOCK_OUTPUT_BUFFER に等しいバッファーを割り当てます。
    • 割り当てられた FLT_CALLBACK_DATAIopb->Parameters.FileSystemControl.Buffered.SystemBuffer メンバーをそのバッファーを指すよう設定します。
    • 割り当てられた FLT_CALLBACK_DATAIopb->Parameters.FileSystemControl.Buffered.InputBufferLengthIopb->Parameters.FileSystemControl.Buffered.OutputBufferLength の各フィールドを、そのバッファーのサイズに設定します。

oplock 要求の書式を設定する方法については、REQUEST_OPLOCK_INPUT_BUFFER 構造体のドキュメントを参照してください。

その後、ファイル システムミニフィルターは FltPerformAsynchronousIo を呼び出し、割り当てられた FLT_CALLBACK_DATACallbackData パラメーターとして渡す必要があります。

要求された oplock を許可できる場合、FltPerformAsynchronousIo 呼び出しは STATUS_PENDING を返します。 このため、同期 I/O に対して oplock が付与されることはありません。 操作は、oplock が壊れるまで完了しません。 操作が完了すると、REQUEST_OPLOCK_OUTPUT_BUFFER には oplock の解除に関する情報が含まれます。

oplock を許可できない場合、ファイル システムは適切なエラー コードを返します。 最も一般的に返されるエラー コードは、STATUS_OPLOCK_NOT_GRANTED と STATUS_INVALID_PARAMETER です。

その他の種類のドライバー

他の種類のドライバーは、ZwFsControlFile呼び出すことができます。

  • FsControlCodeFSCTL_REQUEST_OPLOCKに設定します。
  • InputBuffer パラメーター内の REQUEST_OPLOCK_INPUT_BUFFER 構造体へのポインターを渡し、InputBufferLength パラメーターをそのバッファーのサイズに設定します。
  • OutputBuffer パラメーター内の REQUEST_OPLOCK_OUTPUT_BUFFER 構造体へのポインターを渡し、OutputBufferLength パラメーターをそのバッファーのサイズに設定します。

oplock 要求の書式を設定する方法については、REQUEST_OPLOCK_INPUT_BUFFER 構造体のドキュメントを参照してください。

要求された oplock を許可できる場合、ZwFsControlFile 呼び出しは STATUS_PENDING を返します。 このため、同期 I/O に対して oplock が付与されることはありません。 操作は、oplock が壊れるまで完了しません。 操作が完了すると、REQUEST_OPLOCK_OUTPUT_BUFFER には oplock の解除に関する情報が含まれます。

oplock を許可できない場合、ファイル システムは適切なエラー コードを返します。 最も一般的に返されるエラー コードは、STATUS_OPLOCK_NOT_GRANTED と STATUS_INVALID_PARAMETER です。

Oplock を要求するときの共有違反の回避

Atomic Create-With-Oplock メソッドを使用する

アトミック create-with-oplock は oplock 型ではありません。 むしろ、オープン操作は、ファイルを開いてから oplock を受け取るまでの間に、共有モード違反を引き起こさないようにする手順です。 従来の oplock では、Filter oplock と 2 つのハンドルを開く必要があります。 Windows 7 oplock を使用すると、アプリケーションまたはドライバーは、この手順を使用して任意の種類の oplock を要求でき、1 つのハンドルのみを開く必要があります。

アトミックな create-with-oplock プロシージャを実行するには、次の手順を実行する必要があります。

  1. ファイルを開くには、適宜 FltCreateFileEx2または ZwCreateFile使用します。 CreateOptions パラメーターで、フラグを FILE_OPEN_REQUIRING_OPLOCK渡します。 DesiredAccess パラメーターと ShareAccess パラメーターは、必要に応じて設定できます。 たとえば、DesiredAccess パラメーターセット GENERIC_READ でファイルを読み取ることができるようにし、ShareAccess パラメーターで FILE_SHARE_READ |FILE_SHARE_DELETE フラグを使用して、ファイルを開いている間に他のユーザーがファイルの読み取り、名前変更、および/または削除のマークを付けることができるようにします。
  2. FSCTL_REQUEST_OPLOCK 制御コードを使用して、カーネル モードでの oplock の要求 に関するページで説明されているように、結果のファイル オブジェクトまたはハンドルに対して oplock を要求します。

手順 1 から 2 の間、ファイル に対してファイル システム操作を実行しないでください。 これを行うと、デッドロックが発生する可能性があります。

このプロシージャを使用して要求する最も一般的な oplock は、Read-Handle 型です。 これにより、他の呼び出し元に可能な限り多くの同時アクセスを許可しながら、競合するオープンに共有違反が発生しないようにハンドルを閉じる必要がある場合に通知を受け取ることができます。

レガシーフィルターオプロックの使用

従来の Filter oplock では、他のアプリケーション/クライアントが同じストリームにアクセスしようとしたときにアプリケーションを "バックアウト" することもできます。しかし、アトミックな create-with-oplock メソッドよりも柔軟性が低くなります。 このメカニズムにより、ストリームを開こうとしたときに、ストリームの他のアクセサーが共有違反を受け取ることなく、アプリケーションがストリームにアクセスできるようになります。 共有違反を回避するには、次の 3 つの手順を使用してフィルター操作を要求する必要があります。

  1. FILE_READ_ATTRIBUTESのアクセス権と、FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETEの共有モードでファイルを開く。 この手順で開いたハンドルは、データ アクセス (FILE_READ_DATA) ではなく属性アクセス (FILE_READ_ATTRIBUTES) に対してのみ開かれているため、他のアプリケーションが共有違反を受け取ることはありません。 このハンドルは、フィルター操作ロックを要求するのに適していますが、データ ストリームで実際の I/O を実行する場合には適していません。

  2. 手順 1 のハンドルでフィルターの oplock (FSCTL_REQUEST_FILTER_OPLOCK) を要求します。 この手順で付与された oplock により、oplock 所有者はストリームへのアクセスを試みる他のアプリケーションに対して共有違反を引き起こすことなく、"邪魔にならないようにする" ことができます。

  3. 読み取りアクセス用にファイルを再度開きます。 この手順で開いたハンドルを使用すると、oplock ホルダーはストリームで I/O を実行できます。

NTFS ファイル システムは、FILE_RESERVE_OPFILTER 作成オプション フラグを使用して、この手順の最適化を提供します。 前の手順のステップ 1 でこのフラグが指定されている場合、ファイル システムは手順 2 が失敗すると判断できる場合、STATUS_OPLOCK_NOT_GRANTED で作成要求を失敗させることができます。 手順 1 が成功した場合、作成要求に FILE_RESERVE_OPFILTER が指定されている場合でも、手順 2 が成功するという保証はありません。

Oplocks を付与するための条件

次の表は、oplock を許可するために必要な条件を示しています。

要求の種類 条件

レベル 1

フィルター

バッチ

これは、次の条件がすべて満たされている場合にのみ当てはまります。

  • 要求は、ファイルの特定のストリームに対して行われます。
    • ディレクトリの場合は、STATUS_INVALID_PARAMETERが戻されます。
  • 非同期アクセス用にストリームが開かれます。
    • SYNCHRONOUS アクセス用に開かれた場合、STATUS_OPLOCK_NOT_GRANTED が返されます (oplock は同期 I/O 要求には許可されません)。
  • ファイルのどのストリームにも TxF トランザクションはありません。
    • それ以外では STATUS_OPLOCK_NOT_GRANTED が返されます。
  • ストリームには他に開いているものはありません (同じスレッドによっても)。
    • それ以外では STATUS_OPLOCK_NOT_GRANTED が返されます。

現在の oplock 状態が次の場合:

  • Oplock なし: 要求が許可されます。

  • レベル 2: 元のレベル 2 要求は FILE_OPLOCK_BROKEN_TO_NONE で解除されます。 要求された排他 oplock が許可されます。

  • レベル 1、バッチ、フィルター、読み取り、読み取りハンドル、読み取り/書き込み、または読み取り/書き込みハンドル: STATUS_OPLOCK_NOT_GRANTED が返されます。

[レベル 2]

これは、次の条件がすべて満たされている場合にのみ当てはまります。

  • 要求は、ファイルの特定のストリームに対して行われます。
    • ディレクトリの場合は、STATUS_INVALID_PARAMETERが戻されます。
  • 非同期アクセス用にストリームが開かれます。
    • 同期アクセス用に開かれた場合は、STATUS_OPLOCK_NOT_GRANTEDが返されます。
  • ファイルに TxF トランザクションはありません。
    • それ以外では STATUS_OPLOCK_NOT_GRANTED が返されます。
  • ストリームに現在のバイト範囲ロックはありません。
    • それ以外では STATUS_OPLOCK_NOT_GRANTED が返されます。
    • Windows 7 より前のバージョンでは、オペレーティング システムはストリームが最後に開かれてからバイト範囲ロックが存在したかどうかを確認し、存在していた場合は要求を失敗します。

現在の oplock 状態が次の場合:

  • Oplock なし: 要求が許可されます。

  • レベル 2 または読み取り: 要求が許可されます。 同じストリームに対して複数のレベル 2/読み取りオプロックを同時に付与できます。 複数のレベル 2 (ただし読み取りではない) oplock が同じハンドル上に存在することもあります。
    • 既に読み取りオプロックが付与されているハンドルに対して新たに読み取りオプロックが要求されると、2つ目の読み取りオプロックが付与される前に、最初の読み取りオプロックのIRPがSTATUS_OPLOCK_SWITCHED_TO_NEW_HANDLEで完了します。
  • レベル 1、バッチ、フィルター、読み取りハンドル、読み取り/書き込み、読み取り/書き込みハンドル: STATUS_OPLOCK_NOT_GRANTED が返されます。

既読

これは、次の条件がすべて満たされている場合にのみ当てはまります。

  • 要求は、ファイルの特定のストリームに対して行われます。
  • 非同期アクセス用にストリームが開かれます。
    • 同期アクセス用に開かれた場合は、STATUS_OPLOCK_NOT_GRANTEDが返されます。
  • ファイルに TxF トランザクションはありません。
    • それ以外では STATUS_OPLOCK_NOT_GRANTED が返されます。
  • ストリームに現在のバイト範囲ロックはありません。
    • それ以外では STATUS_OPLOCK_NOT_GRANTED が返されます。
  • ストリームにユーザーがマップしたの書き込み可能なセクションはありません。
    • それ以外は、STATUS_CANNOT_GRANT_REQUESTED_OPLOCK が返されます。 REQUEST_OPLOCK_OUTPUT_BUFFER.Flags フィールドには、REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT フラグが設定されます。

現在の oplock 状態が次の場合:

  • Oplock なし: 要求が許可されます。

  • レベル 2 または読み取り: 要求が許可されます。 同じストリームに対して複数のレベル 2/読み取りオプロックを同時に付与できます。
    • さらに、既存の oplock に新しい要求と同じ oplock キーがある場合、その IRP は STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE で完了します。
  • 読み取りハンドルと既存の oplock には、新しい要求とは異なる oplock キーがあります。要求は許可されます。 複数の読み取りと読み取りハンドルの oplock が同じストリーム上で共存できます (次の表の注を参照)。
    • それ以外の場合 (oplock キーは同じです) は、STATUS_OPLOCK_NOT_GRANTED が返されます。
  • レベル 1、バッチ、フィルター、読み取り/書き込み、読み取り/書き込みハンドル: STATUS_OPLOCK_NOT_GRANTEDが返されます。

読み取りハンドル

これは、次の条件がすべて満たされている場合にのみ当てはまります。

  • 要求は、ファイルの特定のストリームに対して行われます。
  • 非同期アクセス用にストリームが開かれます。
    • 同期アクセス用に開かれた場合は、STATUS_OPLOCK_NOT_GRANTEDが返されます。
  • ファイルに TxF トランザクションはありません。
    • それ以外では STATUS_OPLOCK_NOT_GRANTED が返されます。
  • ストリームに現在のバイト範囲ロックはありません。
    • それ以外では STATUS_OPLOCK_NOT_GRANTED が返されます。
  • ストリームにユーザーがマップしたの書き込み可能なセクションはありません。
    • それ以外は、STATUS_CANNOT_GRANT_REQUESTED_OPLOCK が返されます。 REQUEST_OPLOCK_OUTPUT_BUFFER.Flags フィールドには、REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT フラグが設定されます。

現在の oplock 状態が次の場合:

  • oplock がないため、要求が許可されました。

  • 読み取り: 要求が許可されます。
    • 既存の読み取り oplock に新しい要求と同じ oplock キーがある場合、その IRP は STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE で完了します。 その結果、oplock は読み取りから読み取りハンドルにアップグレードされます。
    • 新しいリクエストと同じ oplock キーを持たない既存の読み取り oplock は変更されません。
  • レベル 2、レベル 1、バッチ、フィルター、読み取り/書き込み、読み取り/書き込みハンドル: STATUS_OPLOCK_NOT_GRANTED が返されます。

読み取り/書き込み

これは、次の条件がすべて満たされている場合にのみ当てはまります。

  • 要求は、ファイルの特定のストリームに対して行われます。
    • ディレクトリの場合は、STATUS_INVALID_PARAMETERが戻されます。
  • 非同期アクセス用にストリームが開かれます。
    • 同期アクセス用に開かれた場合は、STATUS_OPLOCK_NOT_GRANTEDが返されます。
  • ファイルに TxF トランザクションはありません。
    • それ以外では STATUS_OPLOCK_NOT_GRANTED が返されます。
  • ストリームに他のオープンがある場合(同じスレッドであっても)、同じ oplock キーが必要です。
    • それ以外では STATUS_OPLOCK_NOT_GRANTED が返されます。
  • ストリームにユーザーがマップしたの書き込み可能なセクションはありません。
    • それ以外は、STATUS_CANNOT_GRANT_REQUESTED_OPLOCK が返されます。 REQUEST_OPLOCK_OUTPUT_BUFFER.Flags フィールドには、REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT フラグが設定されます。

現在の oplock 状態が次の場合:

  • oplock がないため、要求が許可されました。

  • 読み取りまたは読み取り/書き込みと既存の oplock に要求と同じ oplock キーがある場合、既存の oplock の IRP は STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE で完了し、要求は許可されます。
    • それ以外では STATUS_OPLOCK_NOT_GRANTED が返されます。
  • レベル 2、レベル 1、バッチ、フィルター、読み取りハンドル、読み取り/書き込みハンドル: STATUS_OPLOCK_NOT_GRANTED が返されます。

読み取り/書き込みハンドル

次のすべてが当てはまる場合にのみ許可されます。

  • 要求は、ファイルの特定のストリームに対して行われます。
    • ディレクトリの場合は、STATUS_INVALID_PARAMETERが戻されます。
  • 非同期アクセス用にストリームが開かれます。
    • 同期アクセス用に開かれた場合は、STATUS_OPLOCK_NOT_GRANTEDが返されます。
  • ファイルに TxF トランザクションはありません。
    • それ以外では STATUS_OPLOCK_NOT_GRANTED が返されます。
  • ストリーム上に他の要求が開かれている場合、同じスレッドによるものでも同じ oplock キーを持つ必要があります。
    • それ以外では STATUS_OPLOCK_NOT_GRANTED が返されます。
  • ストリームにユーザーがマップしたの書き込み可能なセクションはありません。
    • それ以外は、STATUS_CANNOT_GRANT_REQUESTED_OPLOCK が返されます。 REQUEST_OPLOCK_OUTPUT_BUFFER.Flags フィールドには、REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT フラグが設定されます。

現在の oplock 状態が次の場合:

  • oplock がないため、要求が許可されました。

  • 読み取り、読み取りハンドル、読み取り/書き込み、または読み取り/書き込みハンドルと既存の oplock に要求と同じ oplock キーがある場合、既存の oplock の IRP は STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE で完了し、後要求が許可されます。
    • それ以外では STATUS_OPLOCK_NOT_GRANTED が返されます。
  • レベル 2、レベル 1、バッチ、フィルター: STATUS_OPLOCK_NOT_GRANTED が返されます。

メモ

読み取りとレベル 2 の oplock は同じストリーム上で共存でき、読み取りと Read-Handle の oplock は共存できますが、レベル 2 と Read-Handle の oplock は共存できません。