Udostępnij przez


Wątki robocze systemu

Sterownik, który wymaga opóźnionego przetwarzania, może użyć elementu roboczego, który zawiera wskaźnik do procedury wywołania zwrotnego sterownika, która wykonuje rzeczywiste przetwarzanie. Sterownik kolejkuje element roboczy, a wątek roboczy systemu usuwa element roboczy z kolejki i uruchamia funkcję zwrotną sterownika. System obsługuje pulę tych wątków procesów roboczych systemu, które są wątkami systemowymi, które przetwarzają jeden element roboczy w danym momencie.

Sterownik kojarzy procedurę wywołania zwrotnego WorkItem z elementem roboczym. Gdy wątek roboczy systemu przetwarza element roboczy, wywołuje skojarzoną rutynę WorkItem. W systemie Windows Vista i nowszych wersjach systemu Windows sterownik może zamiast tego skojarzyć procedurę WorkItemEx z elementem roboczym. Element WorkItemEx przyjmuje parametry, które różnią się od parametrów pobieranych przez element WorkItem .

Procedury WorkItem i WorkItemEx są uruchamiane w kontekście wątku systemowego. Jeśli procedura wysyłania sterownika może być uruchamiana w kontekście wątku trybu użytkownika, ta procedura może wywołać procedurę WorkItem lub WorkItemEx , aby wykonać dowolne operacje wymagające kontekstu wątku systemowego.

Aby użyć elementu roboczego, sterownik wykonuje następujące czynności:

  1. Przydzielanie i inicjowanie nowego elementu roboczego.

    System używa struktury IO_WORKITEM do przechowywania elementu roboczego. Aby przydzielić nową strukturę IO_WORKITEM i zainicjować ją jako element roboczy, sterownik może wywołać metodę IoAllocateWorkItem. W systemie Windows Vista i nowszych wersjach systemu Windows sterownik może również przydzielić własną strukturę IO_WORKITEM i wywołać IoInitializeWorkItem , aby zainicjować strukturę jako element roboczy. (Sterownik powinien wywołać metodę IoSizeofWorkItem , aby określić liczbę bajtów niezbędnych do przechowywania elementu roboczego).

  2. Skojarz procedurę wywołania zwrotnego z elementem roboczym i umieść element roboczy w kolejce, aby był przetwarzany przez wątek roboczy systemu.

    Aby skojarzyć procedurę WorkItem z elementem roboczym i umieścić element roboczy w kolejce, sterownik powinien wywołać IoQueueWorkItem. Aby skojarzyć procedurę WorkItemEx z elementem roboczym i umieścić ten element w kolejce, sterownik powinien wywołać metodę IoQueueWorkItemEx.

  3. Po tym, jak element roboczy nie jest już wymagany, zwolnij go.

    Element roboczy przydzielony przez IoAllocateWorkItem powinien zostać zwolniony przez IoFreeWorkItem. Element pracy zainicjowany przez IoInitializeWorkItem musi być zdezainicjowany przez IoUninitializeWorkItem, zanim będzie można go zwolnić.

    Element roboczy może być niezainicjowany lub zwalniany tylko wtedy, gdy element roboczy nie jest obecnie w kolejce. System usuwa z kolejki zadanie robocze przed wywołaniem procedury zwrotnej zadania roboczego, więc IoFreeWorkItem i IoUninitializeWorkItem można wywołać wewnątrz procedury zwrotnej.

DPC, który musi zainicjować zadanie przetwarzania wymagające długiego przetwarzania lub wykonania wywołania blokującego, powinien delegować przetwarzanie tego zadania do jednego lub więcej elementów pracy. Podczas uruchamiania DPC wszystkie wątki nie mogą działać. Ponadto DPC, który działa na IRQL = DISPATCH_LEVEL, nie może wykonywać wywołań blokujących. Jednak wątek roboczy systemu, który przetwarza element roboczy, działa na poziomie IRQL = PASSIVE_LEVEL. W związku z tym element roboczy może zawierać wywołania blokujące. Na przykład wątek procesu roboczego systemu może czekać na obiekt dyspozytora.

Ponieważ pula wątków procesów roboczych systemu jest ograniczonym zasobem, procedury WorkItem i WorkItemEx mogą być używane tylko w przypadku operacji, które zajmują krótki czas. Jeśli jedna z tych procedur działa zbyt długo (jeśli na przykład zawiera pętlę nieskończoną) lub czeka zbyt długo, system może zakleszczeć. W związku z tym jeśli sterownik wymaga długich okresów opóźnionego przetwarzania, zamiast tego powinien wywołać polecenie PsCreateSystemThread , aby utworzyć własny wątek systemowy.

Nie należy wywoływać funkcji IoQueueWorkItem ani IoQueueWorkItemEx w celu umieszczenia w kolejce zadania, które już tam się znajduje. Może to spowodować uszkodzenie struktur danych systemowych. Jeśli sterownik kolejkuje ten sam element roboczy przy każdym uruchomieniu określonej procedury sterownika, możesz użyć następującej techniki, aby uniknąć kolejkowania elementu roboczego po raz drugi, jeśli znajduje się on już w kolejce:

  • Sterownik utrzymuje listę zadań dla procedury procesu roboczego.
  • Ta lista zadań jest dostępna w kontekście dostarczanym do rutyny pracownika. Rutyna pracownika i wszelkie rutyny sterowników, które modyfikują listę zadań, synchronizują dostęp do listy.
  • Za każdym razem, gdy jest uruchamiana rutyna procesu roboczego, wykonuje wszystkie zadania na liście i usuwa każde zadanie z listy, gdy zadanie zostanie ukończone.
  • Po nadejściu nowego zadania sterownik dodaje to zadanie do listy. Sterownik kolejkuje element zadania tylko wtedy, gdy lista zadań była wcześniej pusta.

Systemowy wątek roboczy usuwa zadanie robocze z kolejki, zanim wywoła wątek roboczy. W związku z tym wątek sterownika może bezpiecznie kolejkować element roboczy ponownie, gdy tylko zacznie działać wątek procesu roboczego.