Freigeben über


Semaphorobjekte

Jeder Treiber kann ein Semaphorobjekt verwenden, um Vorgänge zwischen den vom Treiber erstellten Threads und anderen Treiberroutinen zu synchronisieren. Beispielsweise kann sich ein treiberspezifischer Thread in einen Wartezustand versetzen, wenn keine ausstehenden E/A-Anforderungen für den Treiber vorhanden sind, und die Verteilerroutinen des Treibers können den Semaphor direkt nach der Warteschlange eines IRP auf den Signalzustand setzen.

Die Verteilerroutinen von Treibern der höchsten Ebene, die im Kontext des Threads ausgeführt werden, der einen E/A-Vorgang anfordert, können ein Semaphor verwenden, um eine Ressource zu schützen, die von den Dispatch-Routinen gemeinsam genutzt wird. Die Verteilerroutine für synchrone E/A-Vorgänge auf niedrigerer Ebene kann auch ein Semaphor verwenden, um eine Ressource zu schützen, die von dieser Teilmenge von Dispatch-Routinen oder mit einem vom Treiber erstellten Thread gemeinsam genutzt wird.

Jeder Treiber, der ein Semaphorobjekt verwendet, muss KeInitializeSemaphor aufrufen, bevor es auf das Semaphor wartet oder loslässt. In der folgenden Abbildung wird veranschaulicht, wie ein Treiber mit einem Thread ein Semaphorobjekt verwenden kann.

Diagramm, das auf ein Semaphorobjekt wartet.

Wie in der vorherigen Abbildung dargestellt, muss ein solcher Treiber den Speicher für das Semaphorobjekt bereitstellen, das resident sein sollte. Der Treiber kann die Geräteerweiterung eines vom Treiber erstellten Geräteobjekts, die Controllererweiterung verwenden, wenn ein Controllerobjektoder vom Treiber zugewiesener nichtpageierter Pool verwendet wird.

Wenn das AddDevice Routineaufrufe des Treibers KeInitializeSemaphoraufruft, muss er einen Zeiger an den residenten Speicher des Treibers für das Semaphorobjekt übergeben. Darüber hinaus muss der Aufrufer eine Count für das Semaphorobjekt angeben, wie in der vorherigen Abbildung dargestellt, die den Anfangszustand (Nonzero für Signaled) bestimmt.

Der Aufrufer muss auch ein Limit für das Semaphor angeben, das eine der folgenden Sein kann:

  • Limit = 1

    Wenn dieses Semaphor auf den Signalzustand festgelegt ist, wird ein einzelner Thread, der darauf wartet, dass das Semaphor auf den Signalzustand festgelegt wird, zur Ausführung berechtigt und kann auf jede Ressource zugreifen, die durch das Semaphor geschützt ist.

    Diese Art von Semaphor wird auch als binäre semaphor bezeichnet, da ein Thread entweder über exklusiven Zugriff auf die semaphorgeschützte Ressource verfügt oder nicht.

  • Limit > 1

    Wenn dieses Semaphor auf den Signalzustand festgelegt ist, werden einige Threads, die darauf warten, dass das Semaphorobjekt auf den Signalzustand festgelegt wird, zur Ausführung berechtigt und kann auf jede Ressource zugreifen, die durch das Semaphor geschützt ist.

    Diese Art von Semaphor wird als Zählen von Semaphor bezeichnet, da die Routine, die das Semaphor auf den Signalzustand festlegt, auch angibt, wie viele Wartethreads ihre Zustände von warten auf bereit geändert werden können. Die Anzahl solcher Wartethreads kann die Limit festgelegt werden, wenn das Semaphor initialisiert wurde oder eine Zahl kleiner als diese voreingestellte Limit.

Nur wenige Geräte- oder Zwischentreiber verfügen über einen einzigen treiberbasierten Thread; noch weniger haben eine Reihe von Threads, die warten können, bis ein Semaphor erworben oder freigegeben wird. Nur wenige vom System bereitgestellte Treiber verwenden Semaphorobjekte, und von denen, die dies tun, verwenden sogar weniger ein binäres Semaphor. Obwohl ein binäres Semaphor in der Funktionalität einem Mutex-Objektähnelt, bietet ein binäres Semaphor nicht den integrierten Schutz vor Deadlocks, die ein Mutex-Objekt für Systemthreads hat, die auf SMP-Computern ausgeführt werden.

Nachdem ein Treiber mit einem initialisierten Semaphor geladen wurde, kann er Vorgänge auf dem Semaphor synchronisieren, das eine freigegebene Ressource schützt. Beispielsweise kann ein Treiber mit einem gerätededizierten Thread, der die Warteschlange von IRPs verwaltet, z. B. der System-Diskettencontrollertreiber, IRP-Warteschlangen auf einem Semaphor synchronisieren, wie in der vorherigen Abbildung gezeigt:

  1. Der Thread ruft KeWaitForSingleObject mit einem Zeiger auf den vom Treiber bereitgestellten Speicher auf, damit sich das initialisierte Semaphorobjekt in einen Wartezustand versetzen kann.

  2. IRPs treten auf, bei denen Geräte-E/A-Vorgänge erforderlich sind. Die Verteilerroutinen des Fahrers fügen jedes solche IRP in eine verriegelte Warteschlange unter Drehsperrsteuerung ein und rufen KeReleaseSemaphore mit einem Zeiger auf das Semaphorobjekt auf, eine treiberbestimmte Prioritätsverstärkung für den Thread (Inkrementierung, wie in der vorherigen Abbildung dargestellt), eine Anpassung von 1, die der Anzahl des Semaphors hinzugefügt wird, während jedes IRP in die Warteschlange gestellt wird, und ein boolescher Wait auf FALSEfestgelegt. Ein Nonzero-Semaphoranzahl legt das Semaphorobjekt auf den Signalzustand fest und ändert dadurch den Zustand des Wartethreads auf "Bereit".

  3. Der Kernel verteilt den Thread für die Ausführung, sobald ein Prozessor verfügbar ist: d. h., kein anderer Thread mit höherer Priorität befindet sich derzeit im bereiten Zustand und es gibt keine Kernelmodusroutinen, die bei einem höheren IRQL ausgeführt werden sollen.

    Der Thread entfernt ein IRP aus der verriegelten Warteschlange unter Spin-Lock-Steuerung, übergibt ihn zur weiteren Verarbeitung an andere Treiberroutinen und ruft KeWaitForSingleObject erneut auf. Wenn das Semaphor noch auf den Signalzustand festgelegt ist (d. h. die Anzahl bleibt nicht null, was angibt, dass sich weitere IRPs in der interlockierten Warteschlange des Treibers befinden), ändert der Kernel erneut den Status des Threads von der Wartezeit auf den Start.

    Durch die Verwendung eines Zählsemaphors auf diese Weise ist ein solcher Treiberthread "weiß", dass ein IRP aus der interlockierten Warteschlange entfernt wird, wenn dieser Thread ausgeführt wird.

Informationen zum Verwalten von IRQL beim Aufrufen von KeReleaseSemaphorefinden Sie im Abschnitt "Hinweise" von KeReleaseSemaphore.

Jede Standardtreiberroutine, die mit einer IRQL ausgeführt wird, die größer als PASSIVE_LEVEL kann nicht auf ein Nonzero-Intervall für alle Dispatcherobjekte warten, ohne das System herunterzurücken; Weitere Informationen finden Sie unter Kernel Dispatcher Objects. Eine solche Routine kann jedoch KeReleaseSemaphor aufrufen, während sie bei einer IRQL unter oder gleich DISPATCH_LEVEL ausgeführt wird.

Eine Zusammenfassung der IRQLs, bei denen Standardtreiberroutinen ausgeführt werden, finden Sie unter Verwalten von Hardwareprioritäten. Informationen zu IRQL-Anforderungen einer bestimmten Supportroutine finden Sie auf der Referenzseite der Routine.