共用方式為


要求及授予 oplocks

當網路重導程式存取遠端伺服器上的檔案時,它會向遠端伺服器請求 oplock。 只有當鎖定適用於本地伺服器上的檔案時,用戶端應用程式才會直接請求 oplock。

Oplocks 是透過 FSCTLs要求的。 下列 FSCTL 用於不同的 oplock 類型,兩者均可由使用者模式應用程式和核心模式驅動程式發出。

在使用者模式中要求 oplock

若要在用戶模式中要求 Windows 7 oplock,請調用 DeviceIoControl

如需詳細資訊,請參閱 FSCTL_REQUEST_OPLOCK

如果可以授與要求的 oplock,DeviceIoControl 會傳回 FALSE,GetLastError 會傳回ERROR_IO_PENDING。 因此,同步 I/O 永遠不會被提供鎖定機制。 重疊的作業在 oplock 中斷之前不會完成。 作業完成之後,REQUEST_OPLOCK_OUTPUT_BUFFER 會包含 oplock 中斷的相關資訊。

如果無法獲得 oplock,檔案系統會傳回適當的錯誤碼。 最常傳回的錯誤碼是ERROR_OPLOCK_NOT_GRANTED和ERROR_INVALID_PARAMETER。

核心模式中申請 oplock

若要在核心模式中要求 Windows 7 的 oplocks:

文件系統迷你篩選程式

文件系統迷你篩選器必須使用 FltAllocateCallbackData,並填寫配置的 FLT_CALLBACK_DATA,如下所示:

  • Iopb->MajorFunction 字段設定為 IRP_MJ_FILE_SYSETM_CONTROL
  • 將其 Iopb->MinorFunction 欄位設定為 IRP_MN_USER_FS_REQUEST。
  • 將其 Iopb->參數的 FileSystemControl.Buffered.FsControlCode 成員設定為 FSCTL_REQUEST_OPLOCK
  • 配置大小等於大於 REQUEST_OPLOCK_INPUT_BUFFERREQUEST_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_DATA 做為 CallbackData 參數。

如果可以授與要求的 oplock,FltPerformAsynchronousIo 呼叫會返回STATUS_PENDING。 因此,同步 I/O 永遠不會被提供鎖定機制。 操作在 oplock 中斷之前不會完成。 作業完成之後,REQUEST_OPLOCK_OUTPUT_BUFFER 會包含 oplock 中斷的相關資訊。

如果無法獲得 oplock,檔案系統會傳回適當的錯誤碼。 最常傳回的錯誤碼是STATUS_OPLOCK_NOT_GRANTED和STATUS_INVALID_PARAMETER。

其他類型的驅動程式

其他類型的驅動程式可以呼叫 ZwFsControlFile

若需要知道如何格式化 oplock 請求,請參照有關 REQUEST_OPLOCK_INPUT_BUFFER 結構的文件。

如果可以授與要求的 oplock,ZwFsControlFile 呼叫會傳回 STATUS_PENDING。 因此,同步 I/O 永遠不會被提供鎖定機制。 操作在 oplock 中斷之前不會完成。 作業完成之後,REQUEST_OPLOCK_OUTPUT_BUFFER 會包含 oplock 中斷的相關資訊。

如果無法獲得 oplock,檔案系統會傳回適當的錯誤碼。 最常傳回的錯誤碼是STATUS_OPLOCK_NOT_GRANTED和STATUS_INVALID_PARAMETER。

在請求Oplocks時避免共用違規

使用 Atomic Create-With-Oplock 方法

原子 create-with-oplock 不是一種 oplock 類型。 相反地,這是一種程序,可讓開啟作業避免在開啟檔案與接收 oplock 之間的時間範圍內引發共享模式違規。 使用舊版 oplocks 時,需要篩選 oplocks 和開啟兩個句柄。 透過 Windows 7 oplocks,應用程式或驅動程式可以使用此程式提出任何類型的 oplock 要求,而且只需開啟一個句柄。

若要執行原子性 create-with-oplock 操作程序,您應該:

  1. 視需要使用 FltCreateFileEx2ZwCreateFile開啟檔案。 在 CreateOptions 參數中,將旗標傳遞 FILE_OPEN_REQUIRING_OPLOCK。 您可以視需要設定 DesiredAccessShareAccess 參數。 例如,在 DesiredAccess 參數中設定 GENERIC_READ,以便讀取檔案,並在 ShareAccess 參數中設定 FILE_SHARE_READ | FILE_SHARE_DELETE 旗標,以允許其他人在你開啟檔案時讀取、重新命名及/或標記檔案進行刪除。
  2. 使用 FSCTL_REQUEST_OPLOCK 控制碼向生成的文件對象或操作句柄請求 Oplock,如 在核心模式中請求 Oplock中所述。

請勿在步驟 1 到 2 之間的檔案上執行任何文件系統作業。 這樣做可能會導致死結。

使用此程式要求最常見的 oplock 是 Read-Handle 類型。 這樣,您可以允許其他呼叫者進行最大程度的並行存取,同時在需要關閉控制代碼以避免產生共用衝突時得到通知。

使用舊版過濾器 Oplock

舊版 Filter oplock 也可讓應用程式在其他應用程式/用戶端嘗試存取相同數據流時「退出」,但靈活性不如使用 oplock 的原子創建方法。 此機制可讓應用程式存取串流,而不會造成其他存取者在嘗試開啟串流時發生共用衝突。 若要避免共用違規,應該使用下列三個步驟的程序來申請 Filter oplock:

  1. 以需要的存取模式 FILE_READ_ATTRIBUTES 和 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE 的共用模式開啟檔案。 在此步驟中開啟的句柄不會造成其他應用程式收到共用違規,因為此句柄只會針對屬性存取 (FILE_READ_ATTRIBUTES) 而非數據存取 (FILE_READ_DATA) 開啟。 此句柄適用於要求篩選操作鎖定,但不適合在數據流上執行實際的 I/O。

  2. 在步驟 1 的控制代碼上請求 Filter oplock (FSCTL_REQUEST_FILTER_OPLOCK)。 此步驟中授與的oplock可讓oplock持有者「走出去」,而不會造成嘗試存取數據流的另一個應用程式發生共享違規。

  3. 再次開啟檔案以進行讀取存取。 在此步驟中開啟的控制柄可讓 oplock 持有人在數據流上執行 I/O。

NTFS 檔案系統透過 FILE_RESERVE_OPFILTER 建立選項旗標,為這個過程提供優化。 如果在上一個程序的步驟 1 中指定此標誌,並且文件系統能夠判定步驟 2 將失敗,則文件系統允許建立請求失敗,並返回 STATUS_OPLOCK_NOT_GRANTED。 如果步驟 1 成功,則不保證步驟 2 會成功,即使已為建立要求指定FILE_RESERVE_OPFILTER也一樣。

授予Oplocks的條件

下表列出授予 oplock 所需的必要條件。

要求類型 條件

層級 1

過濾器

只有在下列所有條件都成立時才授與:

  • 要求提供檔案的特定資料流。
    • 如果檢測到是目錄,則會傳回STATUS_INVALID_PARAMETER。
  • 資料流已開啟以便進行非同步存取。
    • 如果以同步方式開啟存取,則會回傳 STATUS_OPLOCK_NOT_GRANTED(因為同步 I/O 請求不會授予 oplocks)。
  • 任何檔案數據流上沒有任何 TxF 交易。
    • 否則會傳回 STATUS_OPLOCK_NOT_GRANTED。
  • 在數據流上沒有其他開啟的情況(即使是由相同執行緒進行)。
    • 否則會傳回 STATUS_OPLOCK_NOT_GRANTED。

如果目前的 oplock 狀態為:

  • 沒有 oplock:請求已獲准。

  • 層級 2:原始的層級 2 要求因 FILE_OPLOCK_BROKEN_TO_NONE 而中斷。 接著便授予了所要求的獨佔鎖定權限。

  • 層級 1、Batch、Filter、Read、Read-Handle、Read-Write 或 Read-Write-Handle:會返回「STATUS_OPLOCK_NOT_GRANTED」。

層級 2

只有在下列所有條件都成立時才授與:

  • 要求提供檔案的特定資料流。
    • 如果檢測到是目錄,則會傳回STATUS_INVALID_PARAMETER。
  • 資料流已開啟以便進行非同步存取。
    • 如果針對 SYNCHRONOUS 存取開啟,則會傳回 STATUS_OPLOCK_NOT_GRANTED。
  • 檔案上沒有 TxF 交易。
    • 否則會傳回 STATUS_OPLOCK_NOT_GRANTED。
  • 當前數據流沒有位元組範圍鎖定。
    • 否則會傳回 STATUS_OPLOCK_NOT_GRANTED。
    • 在 Windows 7 之前,操作系統會驗證自上次開啟串流以來是否曾存在位元組範圍鎖定,如果是這樣,則要求會失敗。

如果目前的 oplock 狀態為:

  • 沒有 oplock:請求已獲准。

  • 層級 2 和/或讀取權限:請求已被批准。 您可以同時在同一資料流中授予多個第二級/讀取型 oplock。 多個二級(但不包括讀取的)oplocks 甚至可以存在於同一個文件上。
    • 如果在已授予 Read oplock 的句柄上再次請求 Read oplock,則第一個 Read oplock 的 IRP 將以 STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE 完成,然後再授予第二個 Read oplock。
  • 當層級為 1、Batch、Filter、Read-Handle、Read-Write 和 Read-Write-Handle 時,系統會傳回 STATUS_OPLOCK_NOT_GRANTED。

只有在下列所有條件都成立時才授與:

  • 要求提供檔案的特定資料流。
  • 資料流已開啟以便進行非同步存取。
    • 如果針對 SYNCHRONOUS 存取開啟,則會傳回 STATUS_OPLOCK_NOT_GRANTED。
  • 檔案上沒有 TxF 交易。
    • 否則會傳回 STATUS_OPLOCK_NOT_GRANTED。
  • 當前數據流沒有位元組範圍鎖定。
    • 否則會傳回 STATUS_OPLOCK_NOT_GRANTED。
  • 資料流上沒有可寫入的 使用者映射區段
    • 否則會傳回STATUS_CANNOT_GRANT_REQUESTED_OPLOCK。 REQUEST_OPLOCK_OUTPUT_BUFFER。欄位 的 REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT 旗標將會被設定。

如果目前的 oplock 狀態為:

  • 沒有 oplock:請求已獲准。

  • 層級 2 和/或讀取權限:請求已被批准。 您可以同時在同一資料流中授予多個第二級/讀取型 oplock。
    • 此外,如果現有的 oplock 與新請求具有相同的 oplock 鍵,其 IRP 會以 STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE 完成。
  • Read-Handle 和現有的 oplock 具有與新要求不同的 oplock 密鑰。請求已被允許。 多個讀取和讀取控制 (Read-Handle) oplock 可以共存於相同的資料流上(請參閱下表後面的附注)。
    • 否則,如果 oplock 鍵相同,則傳回 STATUS_OPLOCK_NOT_GRANTED。
  • 層級 1、Batch、Filter、Read-Write、Read-Write-Handle:會傳回STATUS_OPLOCK_NOT_GRANTED。

Read-Handle

只有在下列所有條件都成立時才授與:

  • 要求提供檔案的特定資料流。
  • 資料流已開啟以便進行非同步存取。
    • 如果針對 SYNCHRONOUS 存取開啟,則會傳回 STATUS_OPLOCK_NOT_GRANTED。
  • 檔案上沒有 TxF 交易。
    • 否則會傳回 STATUS_OPLOCK_NOT_GRANTED。
  • 當前數據流沒有位元組範圍鎖定。
    • 否則會傳回 STATUS_OPLOCK_NOT_GRANTED。
  • 數據流上沒有可寫入 使用者映射區段
    • 否則會傳回STATUS_CANNOT_GRANT_REQUESTED_OPLOCK。 REQUEST_OPLOCK_OUTPUT_BUFFER。欄位 的 REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT 旗標將會被設定。

如果目前的 oplock 狀態為:

  • 無機會鎖定:請求已被批准。

  • 通知:請求已被批准。
    • 如果現有的 Read 鎖控 (oplock) 與新要求具有相同的鎖控鑰匙 (oplock key),其 IRP 會以 STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE 完成。 結果是 oplock 從 Read 升級為 Read-Handle。
    • 任何現有的 Read oplock,如果其 oplock 機碼與新要求不相同,則不會改變。
  • 層級 2、層級 1、Batch、Filter、Read-Write、Read-Write-Handle:將傳回狀態 STATUS_OPLOCK_NOT_GRANTED。

讀寫

只有在下列所有條件都成立時才授與:

  • 要求提供檔案的特定資料流。
    • 如果檢測到是目錄,則會傳回STATUS_INVALID_PARAMETER。
  • 資料流已開啟以便進行非同步存取。
    • 如果針對 SYNCHRONOUS 存取開啟,則會傳回 STATUS_OPLOCK_NOT_GRANTED。
  • 檔案上沒有 TxF 交易。
    • 否則會傳回 STATUS_OPLOCK_NOT_GRANTED。
  • 如果在數據流上有其他開啟的連線(即使是由同一個執行緒建立),它們必須具有相同的 oplock 鑰匙。
    • 否則會傳回 STATUS_OPLOCK_NOT_GRANTED。
  • 數據流上沒有可寫入 用戶對應區段
    • 否則會傳回STATUS_CANNOT_GRANT_REQUESTED_OPLOCK。 REQUEST_OPLOCK_OUTPUT_BUFFER。欄位 的 REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT 旗標將會被設定。

如果目前的 oplock 狀態為:

  • 無機會鎖定:請求已被批准。

  • 讀取或讀寫,並且現有的 oplock 金鑰與要求相同:現有的 oplock 的 IRP 已完成,狀態為 STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE,並允許該要求。
    • 否則會傳回 STATUS_OPLOCK_NOT_GRANTED。
  • 層級 2、層級 1、批次模式、篩選、讀取控制代碼、讀寫控制代碼:返回 STATUS_OPLOCK_NOT_GRANTED。

讀取:Write-Handle

只有在下列所有條件都成立時才授與:

  • 要求提供檔案的特定資料流。
    • 如果檢測到是目錄,則會傳回STATUS_INVALID_PARAMETER。
  • 資料流已開啟以便進行非同步存取。
    • 如果針對 SYNCHRONOUS 存取開啟,則會傳回 STATUS_OPLOCK_NOT_GRANTED。
  • 檔案上沒有 TxF 交易。
    • 否則會傳回 STATUS_OPLOCK_NOT_GRANTED。
  • 如果在資料流上有其他開啟的請求,即使是由相同的執行緒建立的,它們也必須有相同的 oplock 鍵值。
    • 否則會傳回 STATUS_OPLOCK_NOT_GRANTED。
  • 數據流上沒有可寫入的 用戶映射區段
    • 否則會傳回STATUS_CANNOT_GRANT_REQUESTED_OPLOCK。 REQUEST_OPLOCK_OUTPUT_BUFFER。欄位 的 REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT 旗標將會被設定。

如果目前的 oplock 狀態為:

  • 無機會鎖定:請求已被批准。

  • 讀取、讀取-操作、讀寫或讀取-Write-Handle,且現有的 oplock 密鑰與請求相同時:現有 oplock 的 IRP 已完成為 STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE,並批准該請求。
    • 否則會傳回 STATUS_OPLOCK_NOT_GRANTED。
  • 層級 2、層級 1、批次、篩選:傳回STATUS_OPLOCK_NOT_GRANTED。

注意

讀取和Level 2 oplock可以共存於相同的數據流,而讀取和Read-Handle oplocks可以共存,但Level 2和Read-Handle oplocks無法共存。