Udostępnij przez


Żądanie i udzielenie blokad oplocks

Gdy klient sieciowy uzyskuje dostęp do plików na serwerach zdalnych, żąda blokady z serwera zdalnego. Aplikacje klienckie bezpośrednio żądają oplocków tylko wtedy, gdy blokada jest przeznaczona dla pliku na serwerze lokalnym.

Oplocks są żądane za pośrednictwem FSCTLs. Następujące FSCTL są używane dla różnych typów oplock, które mogą wydawać zarówno aplikacje w trybie użytkownika, jak i sterowniki w trybie jądra.

Żądanie blokady w trybie użytkownika

Aby zażądać blokady systemu Windows 7 w trybie użytkownika, wywołaj DeviceIoControl:

Aby uzyskać więcej informacji, zobacz FSCTL_REQUEST_OPLOCK.

Jeśli można udzielić żądanego oplocka, DeviceIoControl zwraca FALSE, a GetLastError zwraca ERROR_IO_PENDING. Z tego powodu oplocks nigdy nie są przyznawane w przypadku synchronicznego we/wy. Nakładana operacja nie zostanie ukończona, dopóki operacja oplock nie zostanie przerwana. Po zakończeniu operacji, REQUEST_OPLOCK_OUTPUT_BUFFER będzie zawierać informacje o przerwaniu blokady.

Jeśli nie można udzielić oplocka, system plików zwraca odpowiedni kod błędu. Najczęściej zwracane kody błędów to ERROR_OPLOCK_NOT_GRANTED i ERROR_INVALID_PARAMETER.

Żądanie oplock w trybie jądra

Aby zażądać blokad systemu Windows 7 w trybie jądra:

Minifiltry systemu plików

Minifiltr systemu plików musi używać FltAllocateCallbackData i wypełnić przydzielone FLT_CALLBACK_DATA w następujący sposób:

  • Ustaw pole Iopb->MajorFunction na wartość IRP_MJ_FILE_SYSTEM_CONTROL.
  • Ustaw pole Iopb->MinorFunction na wartość IRP_MN_USER_FS_REQUEST.
  • Ustaw parametr Iopb ->.FileSystemControl.Buffered.FsControlCode w członku, na FSCTL_REQUEST_OPLOCK.
  • Przydziel bufor, którego rozmiar jest równy większemu rozmiarowi REQUEST_OPLOCK_INPUT_BUFFER lub REQUEST_OPLOCK_OUTPUT_BUFFER.
    • Ustaw przydzielony element członkowski FLT_CALLBACK_DATAIopb->Parameters.FileSystemControl.Buffered.SystemBuffer, aby wskazywał ten bufor.
    • Ustaw w przydzielonym FLT_CALLBACK_DATApolu Iopb->Parameters.FileSystemControl.Buffered.InputBufferLength oraz Iopb->Parameters.FileSystemControl.Buffered.OutputBufferLength wartości odpowiadające rozmiarowi tego bufora.

Zapoznaj się z dokumentacją struktury REQUEST_OPLOCK_INPUT_BUFFER, aby uzyskać informacje na temat formatowania żądania oplocka.

Następnie minifiltr systemu plików musi wywołać FltPerformAsynchronousIo, przekazując przydzielone FLT_CALLBACK_DATA jako parametr CallbackData.

Jeśli żądany oplock zostanie przyznany, wywołanie FltPerformAsynchronousIo zwraca STATUS_PENDING. Z tego powodu oplocks nigdy nie są przyznawane w przypadku synchronicznego we/wy. Operacja nie zostanie ukończona, dopóki operacja nie zostanie przerwana. Po zakończeniu operacji, REQUEST_OPLOCK_OUTPUT_BUFFER będzie zawierać informacje o przerwaniu blokady.

Jeśli nie można udzielić oplocka, system plików zwraca odpowiedni kod błędu. Najczęściej zwracane kody błędów to STATUS_OPLOCK_NOT_GRANTED i STATUS_INVALID_PARAMETER.

Inne rodzaje sterowników

Inne rodzaje sterowników mogą korzystać z ZwFsControlFile:

Zapoznaj się z dokumentacją struktury REQUEST_OPLOCK_INPUT_BUFFER, aby uzyskać informacje na temat formatowania żądania oplocka.

Jeśli można udzielić żądanego oplocka, wywołanie ZwFsControlFile zwraca STATUS_PENDING. Z tego powodu oplocks nigdy nie są przyznawane w przypadku synchronicznego we/wy. Operacja nie zostanie ukończona, dopóki operacja nie zostanie przerwana. Po zakończeniu operacji, REQUEST_OPLOCK_OUTPUT_BUFFER będzie zawierać informacje o przerwaniu blokady.

Jeśli nie można udzielić oplocka, system plików zwraca odpowiedni kod błędu. Najczęściej zwracane kody błędów to STATUS_OPLOCK_NOT_GRANTED i STATUS_INVALID_PARAMETER.

Unikanie naruszeń udostępniania podczas żądania oplocków

Używanie metody Atomic Create-With-Oplock

Atomic create-with-oplock nie jest typem oplocka. Jest to raczej procedura umożliwiająca wykonywanie operacji w celu uniknięcia naruszeń trybu udostępniania w okresie czasu między otwarciem pliku a otrzymaniem oplocka. W przypadku przestarzałych blokad oplocks wymagane jest użycie blokad filtrujących oplocks i otwarcie dwóch uchwytów. W systemie Windows 7 oplocks aplikacja lub sterownik może zażądać dowolnego typu oplock przy użyciu tej procedury i musi otworzyć tylko jeden uchwyt.

Aby wykonać procedurę atomowej operacji create-with-oplock, należy wykonać następujące czynności:

  1. Aby otworzyć plik, użyj FltCreateFileEx2 lub ZwCreateFile. W parametrze CreateOptions przekaż flagę FILE_OPEN_REQUIRING_OPLOCK. Można ustawić parametry DesiredAccess i ShareAccess zgodnie z potrzebami. Na przykład w parametrze DesiredAccess ustawiono GENERIC_READ, aby można było odczytać plik, a w parametrze ShareAccess ustawić FILE_SHARE_READ | FILE_SHARE_DELETE flagi, aby umożliwić innym osobom odczytywanie, zmienianie nazwy i/lub oznaczanie pliku do usunięcia podczas jego otwierania.
  2. Użyj kodu sterującego FSCTL_REQUEST_OPLOCK, aby zażądać oplock na wynikowym obiekcie pliku lub uchwycie, zgodnie z opisem w Żądanie oplock w trybie jądra.

Nie wykonuj żadnych operacji systemu plików w pliku między krokami 1 i 2. Może to spowodować zakleszczenia.

Najczęściej żądanym typem oplock przy użyciu tej procedury jest typ Read-Handle. Dzięki temu można zezwolić innym rozmówcom na jak najwięcej współbieżnego dostępu, podczas gdy nadal możesz być powiadamiany, jeśli trzeba zamknąć gniazdo, aby uniknąć konfliktu z innym otwartym użyciem w przypadku naruszenia zasad współdzielenia.

Korzystanie ze starszego filtru Oplock

Starsza wersja oplock filtru umożliwia również aplikacji wycofanie się, gdy inne aplikacje/klienci próbują uzyskać dostęp do tego samego strumienia, ale jest mniej elastyczna niż niepodzielna metoda tworzenia z oplock. Ten mechanizm umożliwia aplikacji dostęp do strumienia bez powodowania błędów współdzielenia dla innych użytkowników próbujących otworzyć strumień. Aby uniknąć naruszeń udostępniania, należy użyć następującej trzyetapowej procedury w celu zażądania blokady filtru:

  1. Otwórz plik z wymaganym dostępem FILE_READ_ATTRIBUTES i trybem udostępniania FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE. Dojście otwarte w tym kroku nie spowoduje, że inne aplikacje będą otrzymywać naruszenia udostępniania, ponieważ dojście jest otwarte tylko dla dostępu do atrybutów (FILE_READ_ATTRIBUTES), a nie do danych (FILE_READ_DATA). Ten uchwyt jest odpowiedni do żądania blokady filtru, ale nie do wykonywania rzeczywistych operacji we/wy w strumieniu danych.

  2. Zażądaj blokady filtru (FSCTL_REQUEST_FILTER_OPLOCK) dla uchwytu z kroku 1. Oplock przyznany w tym kroku umożliwia posiadaczowi uniknięcie kolizji z inną aplikacją, która próbuje uzyskać dostęp do strumienia, bez powodowania naruszenia zasad współdzielenia.

  3. Otwórz plik ponownie, aby uzyskać dostęp do odczytu. Uchwyt otwarty na tym etapie umożliwia posiadaczowi oplock wykonywanie operacji we/wy w strumieniu.

System plików NTFS zapewnia optymalizację tej procedury przy użyciu opcji tworzenia flagi FILE_RESERVE_OPFILTER. Jeśli ta flaga jest określona w kroku 1 poprzedniej procedury, umożliwia systemowi plików odmowę żądania utworzenia ze statusem STATUS_OPLOCK_NOT_GRANTED, jeśli system plików może określić, że krok 2 zakończy się niepowodzeniem. Jeśli krok 1 zakończy się pomyślnie, nie ma gwarancji, że krok 2 również się powiedzie, nawet jeśli dla żądania utworzenia wskazano FILE_RESERVE_OPFILTER.

Warunki przyznawania blokad oplocks

W poniższej tabeli przedstawiono wymagane warunki niezbędne do udzielenia oploka.

Typ żądania Warunki

Poziom 1

Filtr

Seria

Przyznane tylko wtedy, gdy spełnione są wszystkie następujące warunki:

  • Żądanie dotyczy danego strumienia pliku.
    • Jeśli zostanie napotkany katalog, zwracany jest STATUS_INVALID_PARAMETER.
  • Strumień jest otwarty do dostępu asynchronicznego.
    • Jeśli otworzysz w trybie dostępu synchronicznego, zostanie zwrócony STATUS_OPLOCK_NOT_GRANTED (blokady operacji nie są przyznawane dla synchronicznych żądań we/wy).
  • W żadnym strumieniu pliku nie ma transakcji TxF.
    • W przeciwnym razie zwracany jest STATUS_OPLOCK_NOT_GRANTED.
  • W strumieniu nie ma żadnych innych otwarć (nawet przez ten sam wątek).
    • W przeciwnym razie zwracany jest STATUS_OPLOCK_NOT_GRANTED.

Jeśli bieżący stan oplock to:

  • Brak oplock: Żądanie zostało udzielone.

  • Poziom 2: Oryginalne żądanie poziomu 2 zostało przerwane z FILE_OPLOCK_BROKEN_TO_NONE. Następnie udzielono żądanego wyłącznego blokowania oplock.

  • Poziom 1, Batch, Filter, Read, Read-Handle, Read-Write lub Read-Write-Handle: STATUS_OPLOCK_NOT_GRANTED jest zwracany.

Poziom 2

Przyznane tylko wtedy, gdy spełnione są wszystkie następujące warunki:

  • Żądanie dotyczy danego strumienia pliku.
    • Jeśli zostanie napotkany katalog, zwracany jest STATUS_INVALID_PARAMETER.
  • Strumień jest otwarty do dostępu asynchronicznego.
    • Jeśli zostanie otwarty w celu uzyskania dostępu synchronicznego, zwrócony zostanie STATUS_OPLOCK_NOT_GRANTED.
  • W pliku nie ma żadnych transakcji TxF.
    • W przeciwnym razie zwracany jest STATUS_OPLOCK_NOT_GRANTED.
  • Brak bieżących blokad zakresu bajtów w strumieniu.
    • W przeciwnym razie zwracany jest STATUS_OPLOCK_NOT_GRANTED.
    • Przed systemem Windows 7 system operacyjny sprawdza, czy blokada zakresu bajtów kiedykolwiek istniała w strumieniu od czasu ostatniego otwarcia, i jeśli tak, kończy się niepowodzeniem żądania.

Jeśli bieżący stan oplock to:

  • Brak oplock: Żądanie zostało udzielone.

  • Poziom 2 i/lub Odczyt: żądanie zostało przyznane. W tym samym czasie można mieć wiele oplocków poziomu 2/do odczytu na tym samym strumieniu. Wiele poziomów blokad operacyjnych 2 (ale nie do odczytu) może współistnieć na tym samym dojściu.
    • Jeśli dla uchwytu jest wymagany oplock odczytu, który ma już przyznany oplock odczytu, IRP pierwszego oplocka odczytu jest ukończony ze statusem STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE przed udzieleniem drugiego oplocka odczytu.
  • Poziom 1, Batch, Filter, Read-Handle, Read-Write, Read-Write-Handle: STATUS_OPLOCK_NOT_GRANTED jest zwracany.

Przeczytaj

Przyznane tylko wtedy, gdy spełnione są wszystkie następujące warunki:

  • Żądanie dotyczy danego strumienia pliku.
  • Strumień jest otwarty do dostępu asynchronicznego.
    • Jeśli zostanie otwarty w celu uzyskania dostępu SYNCHRONICZNEGO, zostanie zwrócony STATUS_OPLOCK_NOT_GRANTED.
  • W pliku nie ma żadnych transakcji TxF.
    • W przeciwnym razie zwracany jest STATUS_OPLOCK_NOT_GRANTED.
  • Brak bieżących blokad zakresu bajtów w strumieniu.
    • W przeciwnym razie zwracany jest STATUS_OPLOCK_NOT_GRANTED.
  • Brak zapisywalnych mapowanych przez użytkownika sekcji w strumieniu.
    • W przeciwnym razie zostanie zwrócony STATUS_CANNOT_GRANT_REQUESTED_OPLOCK. The REQUEST_OPLOCK_OUTPUT_BUFFER.Flags pole będzie mieć ustawioną flagę REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT.

Jeśli bieżący stan oplock to:

  • Brak oplock: Żądanie zostało udzielone.

  • Poziom 2 i/lub Odczyt: żądanie zostało przyznane. W tym samym czasie można mieć wiele oplocków poziomu 2/do odczytu na tym samym strumieniu.
    • Ponadto, jeśli istniejący oplock ma ten sam klucz oplock co nowe żądanie, jego IRP jest ukończony ze statusem STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE.
  • Read-Handle i istniejący oplock ma inny klucz oplock od nowego żądania. Żądanie zostaje przyjęte. Wiele operacji odczytu i Read-Handle może współistnieć na tym samym strumieniu (zobacz notatkę poniżej tej tabeli).
    • W przeciwnym razie (klucze oplock są takie same) zostanie zwrócony STATUS_OPLOCK_NOT_GRANTED.
  • Poziom 1, Batch, Filter, Read-Write, Read-Write-Handle: STATUS_OPLOCK_NOT_GRANTED jest zwracany.

Read-Handle

Przyznane tylko wtedy, gdy spełnione są wszystkie następujące warunki:

  • Żądanie dotyczy danego strumienia pliku.
  • Strumień jest otwarty do dostępu asynchronicznego.
    • Jeśli zostanie otwarty w celu uzyskania dostępu SYNCHRONICZNEGO, zostanie zwrócony STATUS_OPLOCK_NOT_GRANTED.
  • W pliku nie ma żadnych transakcji TxF.
    • W przeciwnym razie zwracany jest STATUS_OPLOCK_NOT_GRANTED.
  • Brak bieżących blokad zakresu bajtów w strumieniu.
    • W przeciwnym razie zwracany jest STATUS_OPLOCK_NOT_GRANTED.
  • Brak zapisywalnych mapowanych przez użytkownika sekcji w strumieniu.
    • W przeciwnym razie zostanie zwrócony STATUS_CANNOT_GRANT_REQUESTED_OPLOCK. The REQUEST_OPLOCK_OUTPUT_BUFFER.Flags pole będzie mieć ustawioną flagę REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT.

Jeśli bieżący stan oplock to:

  • Brak oplock: żądanie zostało udzielone.

  • Przeczytaj: żądanie zostanie przyznane.
    • Jeśli istniejący oplock do odczytu ma ten sam klucz oplock co nowe żądanie, jego IRP jest zakończony statusem STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE. W rezultacie operacja oplock jest uaktualniana z odczytu do dojścia do odczytu.
    • Każda istniejąca operacja odczytu, która nie ma tego samego klucza oplock co nowe żądanie, pozostaje niezmieniona.
  • Poziom 2, Poziom 1, Partia, Filtr, Read-Write, Read-Write-Handle: STATUS_OPLOCK_NOT_GRANTED jest zwracany.

Odczyt-Zapis

Przyznane tylko wtedy, gdy spełnione są wszystkie następujące warunki:

  • Żądanie dotyczy danego strumienia pliku.
    • Jeśli zostanie napotkany katalog, zwracany jest STATUS_INVALID_PARAMETER.
  • Strumień jest otwarty do dostępu asynchronicznego.
    • Jeśli zostanie otwarty w celu uzyskania dostępu SYNCHRONICZNEGO, zostanie zwrócony STATUS_OPLOCK_NOT_GRANTED.
  • W pliku nie ma żadnych transakcji TxF.
    • W przeciwnym razie zwracany jest STATUS_OPLOCK_NOT_GRANTED.
  • Jeśli w strumieniu są inne otwarcia (nawet przez ten sam wątek), muszą mieć ten sam klucz oplock.
    • W przeciwnym razie zwracany jest STATUS_OPLOCK_NOT_GRANTED.
  • Brak zapisywalnych mapowanych przez użytkownika sekcji w strumieniu.
    • W przeciwnym razie zostanie zwrócony STATUS_CANNOT_GRANT_REQUESTED_OPLOCK. The REQUEST_OPLOCK_OUTPUT_BUFFER.Flags pole będzie mieć ustawioną flagę REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT.

Jeśli bieżący stan oplock to:

  • Brak oplock: żądanie zostało udzielone.

  • Odczyt lub Read-Write i istniejący oplock ma ten sam klucz oplock co żądanie: IRP istniejącego oplocka jest ukończone z kodem STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE, a żądanie jest przyznane.
    • W przeciwnym razie zwracany jest STATUS_OPLOCK_NOT_GRANTED.
  • Poziom 2, Poziom 1, Zestaw, Filtr, Read-Handle, Read-Write-Handle: STATUS_OPLOCK_NOT_GRANTED jest zwracany.

Odczyt — Zapis — Obsługa

Przyznane tylko wtedy, gdy wszystkie następujące elementy są spełnione:

  • Żądanie dotyczy danego strumienia pliku.
    • Jeśli zostanie napotkany katalog, zwracany jest STATUS_INVALID_PARAMETER.
  • Strumień jest otwarty do dostępu asynchronicznego.
    • Jeśli plik zostanie otwarty w trybie dostępu SYNCHRONICZNEGO, zostanie zwrócony kod STATUS_OPLOCK_NOT_GRANTED.
  • W pliku nie ma żadnych transakcji TxF.
    • W przeciwnym razie zwracany jest STATUS_OPLOCK_NOT_GRANTED.
  • Jeśli w strumieniu istnieją inne otwarte żądania, muszą mieć ten sam klucz oplock, nawet jeśli pochodzą z tego samego wątku.
    • W przeciwnym razie zwracany jest STATUS_OPLOCK_NOT_GRANTED.
  • Brak zapisywalnych mapowanych przez użytkownika sekcji w strumieniu.
    • W przeciwnym razie zostanie zwrócony STATUS_CANNOT_GRANT_REQUESTED_OPLOCK. The REQUEST_OPLOCK_OUTPUT_BUFFER.Flags pole będzie mieć ustawioną flagę REQUEST_OPLOCK_OUTPUT_FLAG_WRITABLE_SECTION_PRESENT.

Jeśli bieżący stan oplock to:

  • Brak oplock: żądanie zostało udzielone.

  • Odczyt, odczyt-przechwytywanie, odczyt-zapis lub odczyt-Write-Handle, a istniejący oplock ma ten sam klucz oplock co żądanie: IRP istniejącego oplocka zostaje ukończone z STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE i żądanie zostaje przyznane.
    • W przeciwnym razie zwracany jest STATUS_OPLOCK_NOT_GRANTED.
  • Poziom 2, Poziom 1, Batch, Filtr: STATUS_OPLOCK_NOT_GRANTED jest zwracany.

Notatka

Oplocki odczytu i poziomu 2 mogą współistnieć na tym samym strumieniu, a oplocki odczytu i Read-Handle mogą współistnieć, ale oplocki poziomu 2 i Read-Handle nie mogą współistnieć.