Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Sterowniki sieciowe systemu Windows używają żądań OID do wysyłania komunikatów kontrolnych w stosie wiązania NDIS. Sterowniki protokołów, takie jak TCPIP lub vSwitch, polegają na dziesiątkach identyfikatorów OID w celu skonfigurowania każdej funkcji podstawowego sterownika karty sieciowej. Przed systemem Windows 10 w wersji 1709 żądania OID zostały wysłane na dwa sposoby: Regularne i Bezpośrednie.
W tym temacie przedstawiono trzeci styl wywołania identyfikatora OID: Synchroniczne. Wywołanie synchroniczne ma być niskoopóźnieniowe, nieblokujące, skalowalne i niezawodne. Interfejs żądania synchronicznego identyfikatora OID jest dostępny od wersji 6.80 NDIS, która jest zawarta w systemie Windows 10 w wersji 1709 lub nowszej.
Porównanie z regularnymi i bezpośrednimi żądaniami OID
W przypadku synchronicznych żądań OID, ładunek wywołania (sam identyfikator OID) jest dokładnie taki sam jak w przypadku żądań Regularnych i bezpośrednich OID. Jedyną różnicą jest samo wywołanie. W związku z tym to, co jest takie samo we wszystkich trzech typach identyfikatorów OID; tylko sposób jest inny.
W poniższej tabeli opisano różnice między zwykłymi OIDs, bezpośrednimi OIDs i synchronicznymi OIDs.
| Attribute | Zwykły OID | Bezpośredni OID | Synchroniczny OID |
|---|---|---|---|
| Ładunek | NDIS_OID_REQUEST | NDIS_OID_REQUEST | NDIS_OID_REQUEST |
| Typy OID | Statystyki, Zapytanie, Zbiór, Metoda | Statystyki, Zapytanie, Zbiór, Metoda | Statystyki, Zapytanie, Zestaw, Metoda |
| Może zostać wystawiony przez | Protokoły, filtry | Protokoły, filtry | Protokoły, filtry |
| Może być ukończone przez | Miniporty, filtry | Miniporty, filtry | Miniporty, filtry |
| Filtry mogą modyfikować | Tak | Tak | Tak |
| NDIS przydziela pamięć | Dla każdego filtra (klon OID) | Dla każdego filtru (klon OID) | Tylko jeśli liczba filtrów jest wyjątkowo duża (kontekst wywołania) |
| Może być zawieszone | Tak | Tak | Nie. |
| Może blokować | Tak | Nie. | Nie. |
| IRQL | == PASYWNE | <= WYŚLIJ | <= WYŚLIJ |
| Serializowany przez NDIS | Tak | Nie. | Nie. |
| Filtry są wywoływane | Rekursywnie | Rekursywnie | Iteracyjnie |
| Filtry klonują identyfikator OID | Tak | Tak | Nie. |
Filtrowanie
Podobnie jak w przypadku pozostałych dwóch typów wywołań OID, sterowniki filtrów mają pełną kontrolę nad żądaniem OID w wywołaniu synchronicznym. Sterowniki filtrów mogą obserwować, przechwytywać, modyfikować i wystawiać synchroniczne identyfikatory obiektów. Jednak pod kątem wydajności mechanika synchronicznego identyfikatora OID jest nieco inna.
Przekazywanie, przechwytywanie i pochodzenie
Koncepcyjnie wszystkie żądania OID są wydawane z wyższego sterownika i są wykonywane przez niższy sterownik. Po drodze żądanie OID może przechodzić przez dowolną liczbę sterowników filtrów.
W najczęstszym przypadku sterownik protokołu wystawia żądanie identyfikatora OID, a wszystkie filtry po prostu przekazują żądanie OID w dół, niezmodyfikowane. Na poniższej ilustracji przedstawiono ten typowy scenariusz.
Jednak każdy moduł filtru może przechwycić żądanie OID i ukończyć je. W takim przypadku żądanie nie przechodzi do niższych sterowników, jak pokazano na poniższym diagramie.
W niektórych przypadkach moduł filtru może postanowić utworzyć swoje własne żądanie OID. To żądanie rozpoczyna się na poziomie modułu filtru i przechodzi tylko niższe sterowniki, jak pokazano na poniższym diagramie.
Wszystkie żądania OID mają ten podstawowy przepływ: wyższy sterownik (protokół lub sterownik filtru) wystawia żądanie, a niższy sterownik (miniport lub sterownik filtru) kończy je.
Jak działają zwykłe i bezpośrednie żądania OID
Regularne lub bezpośrednie żądania OID są wysyłane rekursywnie. Na poniższym diagramie przedstawiono sekwencję wywołań funkcji. Należy pamiętać, że sama sekwencja jest podobna do sekwencji opisanej na diagramach z poprzedniej sekcji, ale jest ułożone w celu pokazania cyklicznego charakteru żądań.
Jeśli zainstalowano wystarczającą liczbę filtrów, usługa NDIS zostanie zmuszona do przydzielenia nowego stosu wątku, aby kontynuować głębszą rekursję.
NDIS uważa strukturę NDIS_OID_REQUEST za ważną tylko dla jednego przejścia w stosie. Jeśli sterownik filtru chce przekazać żądanie do następnego niższego sterownika (co ma miejsce w przypadku zdecydowanej większości identyfikatorów OID), sterownik filtru musi wstawić kilkadziesiąt wierszy kodu standardowego, aby sklonować żądanie OID. Ten szablon posiada kilka problemów:
- Wymusza alokację pamięci w celu sklonowania identyfikatora OID. Dostęp do puli pamięci jest zarówno powolny, jak i uniemożliwia zagwarantowanie postępu żądania OID.
- Projekt struktury OID musi pozostawać taki sam w czasie, ponieważ wszystkie sterowniki filtrów trwale kodują mechanikę kopiowania zawartości jednej NDIS_OID_REQUEST do innej.
- Wymóg tak dużej ilości kodu szablonowego zaciemnia, co filtr naprawdę robi.
Model filtrowania dla synchronicznych żądań OID
Model filtrowania dla synchronicznych żądań OID korzysta z synchronicznego charakteru wywołania, aby rozwiązać problemy omówione w poprzedniej sekcji.
Procedury wydawania i ukończenia obsługi
W przeciwieństwie do zwykłych i bezpośrednich żądań OID, dla synchronicznych żądań OID istnieją dwa hooki filtrujące: procedura obsługi zgłoszenia i procedura końcowa. Sterownik filtru nie może zarejestrować ani jednego, ani obu haków.
Wywołania są wykonywane dla każdego sterownika filtru, począwszy od góry stosu do końca. Każde wywołanie funkcji filtru może zatrzymać dalsze przetwarzanie OID i zakończyć OID, zwracając kod stanu. Jeśli żaden filtr nie zdecyduje się przechwycić OID, to OID trafi do sterownika karty sieciowej, który musi zakończyć OID synchronicznie.
Po zakończeniu operacji OID, wywoływane są wywołania funkcji Complete dla każdego sterownika filtru, począwszy od punktu w stosie, gdzie OID został ukończony, aż do góry stosu. Kompletne wywołanie może sprawdzać lub modyfikować żądanie OID oraz sprawdzać lub modyfikować kod zakończenia OID.
Na poniższym diagramie przedstawiono typowy przypadek, w którym protokół wystawia żądanie synchronicznego identyfikatora OID, a filtry nie przechwytują żądania.
Należy pamiętać, że model wywołania dla synchronizowanych identyfikatorów OID jest iteracyjny. Dzięki temu użycie stosu jest ograniczone do stałej wartości, eliminując potrzebę rozszerzania stosu.
Jeśli sterownik filtru przechwytuje synchroniczny identyfikator OID w procedurze obsługi problemu, identyfikator OID nie jest podawany do niższych filtrów ani sterownika karty sieciowej. Jednak pełne procedury obsługi dla wyższych filtrów są nadal wywoływane, jak pokazano na poniższym diagramie:
Minimalne alokacje pamięci
Regularne i bezpośrednie żądania OID wymagają, aby sterownik filtrujący sklonował NDIS_OID_REQUEST. Z kolei synchroniczne żądania OID nie mogą być klonowane. Zaletą tego projektu jest to, że synchroniczne identyfikatory OID mają mniejsze opóźnienie — żądanie OID nie jest wielokrotnie klonowane, gdy przechodzi w dół stosu filtru — i istnieje mniej możliwości niepowodzenia.
Jednak to rodzi nowy problem. Jeśli nie można sklonować identyfikatora OID, gdzie sterownik filtru przechowuje jego stan na żądanie? Załóżmy na przykład, że sterownik filtru tłumaczy jeden identyfikator OID na inny. Podczas schodzenia w dół stosu, filtr musi zachować stary identyfikator OID. Podczas powrotu w górę stosu filtr musi przywrócić stary identyfikator OID.
Aby rozwiązać ten problem, NDIS przydziela gniazdo o rozmiarze wskaźnika dla każdego sterownika filtru dla każdego żądania synchronicznego identyfikatora OID w locie. Usługa NDIS zachowuje to miejsce w całym wywołaniu z programu obsługi problemu filtru do jego kompletnej procedury obsługi. Dzięki temu program obsługi problemu może zapisać stan, który jest później używany przez program obsługi Complete. Poniższy fragment kodu przedstawia przykład.
NDIS_STATUS
MyFilterSynchronousOidRequest(
_In_ NDIS_HANDLE FilterModuleContext,
_Inout_ NDIS_OID_REQUEST *OidRequest,
_Outptr_result_maybenull_ PVOID *CallContext)
{
if ( . . . should intercept this OID . . . )
{
// preserve the original buffer in the CallContext
*CallContext = OidRequest->DATA.SET_INFORMATION.InformationBuffer;
// replace the buffer with a new one
OidRequest->DATA.SET_INFORMATION.InformationBuffer = . . . something . . .;
}
return NDIS_STATUS_SUCCESS;
}
VOID
MyFilterSynchronousOidRequestComplete(
_In_ NDIS_HANDLE FilterModuleContext,
_Inout_ NDIS_OID_REQUEST *OidRequest,
_Inout_ NDIS_STATUS *Status,
_In_ PVOID CallContext)
{
// if the context is not null, we must have replaced the buffer.
if (CallContext != null)
{
// Copy the data from the miniport back into the protocol’s original buffer.
RtlCopyMemory(CallContext, OidRequest->DATA.SET_INFORMATION.InformationBuffer,...);
// restore the original buffer into the OID request
OidRequest->DATA.SET_INFORMATION.InformationBuffer = CallContext;
}
}
Funkcja NDIS oszczędza jeden identyfikator PVOID dla każdego filtru przy każdym wywołaniu. NDIS heurystycznie przydziela rozsądną liczbę miejsc w stosie, tak aby w typowym przypadku nie dochodziło do alokacji puli. Zwykle nie jest to więcej niż siedem filtrów. Jeśli użytkownik ustawia przypadek patologiczny, usługa NDIS przełącza się z powrotem na alokację puli.
Zredukowany szablon kodu
Rozważ standardowy element na przykładowej płyty standardowej do obsługi regularnych lub bezpośrednich żądań OID. Ten kod jest kosztem wejścia, aby zarejestrować procedurę obsługi identyfikatora OID. Jeśli chcesz wydać własne identyfikatory obiektów, musisz dodać kilkanaście linii kodu szablonowego. W przypadku synchronicznych OID nie ma potrzeby dodatkowej złożoności obsługi uzupełniania asynchronicznego. W związku z tym można wyciąć znaczną część tego szablonu.
Oto minimalny menedżer problemów z synchronicznymi OID-ami:
NDIS_STATUS
MyFilterSynchronousOidRequest(
NDIS_HANDLE FilterModuleContext,
NDIS_OID_REQUEST *OidRequest,
PVOID *CallContext)
{
return NDIS_STATUS_SUCCESS;
}
Jeśli chcesz przechwycić lub zmodyfikować określony identyfikator OID, możesz to zrobić, dodając tylko kilka wierszy kodu. Minimalna procedura kompletnej obsługi jest jeszcze prostsza:
VOID
MyFilterSynchronousOidRequestComplete(
NDIS_HANDLE FilterModuleContext,
NDIS_OID_REQUEST *OidRequest,
NDIS_STATUS *Status,
PVOID CallContext)
{
return;
}
Podobnie, sterownik filtrujący może wydać własne synchroniczne żądanie OID, używając tylko jednego wiersza kodu:
status = NdisFSynchronousOidRequest(binding->NdisBindingHandle, &oid);
Natomiast sterownik filtru, który musi wydać zwykły lub bezpośredni identyfikator OID, musi ustawić asynchroniczny program obsługi zakończeń i zaimplementować kod, aby odróżnić swoje zakończenia identyfikatorów OID od zakończeń tych, które właśnie sklonował. Przykład tego szablonu jest wyświetlany na przykładzie szablonu do wystawiania regularnego żądania OID.
Współdziałanie
Mimo że Style wywołań Regularnych, Bezpośrednich i Synchronicznych używają tych samych struktur danych, rurociągi nie przechodzą do tej samej procedury obsługi w miniport. Ponadto nie wszystkich identyfikatorów OID można używać w niektórych kanałach. Na przykład OID_PNP_SET_POWER wymaga starannej synchronizacji i często zmusza miniport do wykonywania blokujących wywołań. Sprawia to, że obsługa wywołania zwrotnego bezpośredniego OID jest trudna i uniemożliwia jego użycie w wywołaniu zwrotnym synchronicznego OID.
W związku z tym, podobnie jak w przypadku bezpośrednich żądań OID, synchroniczne wywołania OID mogą być używane tylko z podzbiorem identyfikatorów OID. W systemie Windows 10 w wersji 1709 tylko identyfikator OID OID_GEN_RSS_SET_INDIRECTION_TABLE_ENTRIES, używany w Receive Side Scaling wersji 2 (RSSv2), jest obsługiwany w synchronicznej ścieżce OID.
Implementowanie synchronicznych żądań OID
Aby uzyskać więcej informacji na temat implementowania synchronicznego interfejsu żądania OID w sterownikach, zobacz następujące tematy:
- Żądania OID adaptera miniportowego
- Żądania OID modułu filtrującego
- Żądania OID Sterownika Protokołu