Synchronisierungsbeispiele
Die folgenden Beispiele veranschaulichen, was ein Minidriver in Bezug auf die Synchronisierung tun muss, und enthält Beispiele dafür, wann die Synchronisierung nicht verwendet werden sollte:
Beispiel 1: Minidriver mit funktionierendem ISR
Wenn die Streamklassensynchronisierung aktiviert ist, werden alle Minidriver-Einstiegspunkte bei ausgelöstem IRQL mit KeSynchronizeExecution aufgerufen, was bedeutet, dass die IRQ-Ebene des Adapters und alle niedrigeren IRQ-Ebenen maskiert werden, wenn der Minidriver seinen Code ausführt. Daher ist es zwingend erforderlich, dass der Minidriver nur wenige Aufgaben in diesem Modus ausführt.
Der Minidriver sollte keinen Code ausführen, der in der Regel mehr als 10 bis 20 Mikrosekunden bei ausgelösten IRQL benötigt. Wenn Sie den Debugbuild von stream.sys verwenden, protokolliert die Streamklasse den Zeitraum, der bei ausgelöstem IRQL aufgewendet wurde, und bestätigt, ob der Treiber zu viel Zeit dort verbringt. Wenn der Minidriver einfach Hardware-DMA-Registrierungen für eine Anforderung programmieren muss oder nur Ports in seinem ISR lesen muss, ist es in der Regel akzeptabel, alle Verarbeitungen bei ausgelöstem IRQL auszuführen.
Wenn der Minidriver die Verarbeitung durchführen muss, die mehr als ein paar Mikrosekunden benötigt, z. B. ein Minidriver, der Daten über PIO überträgt, sollte der Minidriver StreamClassCallAtNewPriority verwenden, um einen DISPATCH_LEVEL Rückruf zu planen. Im Rückruf kann der Minidriver bis zu 1/2 bis 1 Millisekunden dauern, um die Verarbeitung zu erledigen. Beachten Sie bei diesem Modus, dass der DISPATCH_LEVEL Rückruf nicht mit dem ISR synchronisiert wird.
Dieser Mangel an Synchronisierung ist kein Problem, wenn die Hardware stabil bleibt, wenn der Minidriver während des Rückrufs und im ISR auf Ressourcen (z. B. Ports oder eine Warteschlange) zugreift. Wenn instabilität jedoch ein Problem sein könnte, muss der Minidriver StreamClassCallAtNewPriority verwenden, um einen Rückruf mit hoher Priorität zu planen, bei dem der DISPATCH_LEVEL Rückruf Ressourcen berührt, die mit den vom ISR verwendeten Ressourcen geteilt werden.
Beachten Sie, dass ein Rückruf mit hoher Priorität dem Aufrufen von KeSynchronizeExecution entspricht. KeSynchronizeExecution erfordert den Minidriver, um auf mehrere Parameter zu verweisen, die StreamClassCallAtNewPriority nicht, sondern im Allgemeinen die beiden Ergebnisse in demselben Verhalten ergeben.
Wenn der Minidriver nur gelegentlich Code ausführen muss, der mehr als 1/2 bis 1 Millisekunden benötigt, oder gelegentlich Dienste bei PASSIVE_LEVEL aufrufen muss (z. B. zur Initialisierungszeit), kann das Festlegen von StreamClassCallAtNewPriority auf LOW-Priorität verwendet werden, um einen PASSIVE_LEVEL Arbeitsthread abzurufen. Beachten Sie, dass ein Rückruf mit niedriger Priorität nicht mit allem synchronisiert wird und dass der Minidriver neue Anforderungen empfangen kann (vorausgesetzt , der Parameter ReadyForNextRequest NotificationType steht aus) oder ein ISR-Aufruf, wenn ein Rückruf mit niedriger Priorität ausgeführt wird.
Beispiel 2: Minidriver ohne ISR
Wenn die Streamklassensynchronisierung aktiviert ist, werden alle Einstiegspunkte des Minidrivers an DISPATCH_LEVEL aufgerufen. Der Minidriver kann die Verarbeitung von bis zu 1/2 bis 1 Millisekunden dauern, ohne die Priorität anpassen zu müssen. Wenn der Minidriver nur gelegentlich Code ausführen muss, der mehr als 1/2 Millisekunden benötigt oder gelegentlich Dienste bei PASSIVE_LEVEL aufrufen muss (z. B. zur Initialisierungszeit), kann StreamClassCallAtNewPriority mit NIEDRIGER Priorität verwendet werden, um einen PASSIVE_LEVEL Workerthread abzurufen. Beachten Sie, dass ein Rückruf mit niedriger Priorität nicht mit allem synchronisiert wird, und der Minidriver könnte neue Anforderungen empfangen (vorausgesetzt , readyForNextRequest NotificationType-Parameter steht aus), wenn ein Rückruf mit niedriger Priorität ausgeführt wird.
Wenn die Streamklassensynchronisierung nicht verwendet werden sollte
Im Folgenden finden Sie Beispiele für Situationen, in denen die Streamklassensynchronisierung nicht verwendet werden sollte. Dazu gehören:
Wenn Treiber häufig (mehr als 20 Prozent der Anforderungen, die der Minidriver empfängt), eine Verarbeitung durchführen müssen, die mehr als 1 Millisekunden benötigt, oder häufig PASSIVE_LEVEL Dienste aufrufen müssen, z. B. Microsoft DirectDraw-Dienste. Wenn Sie die Debugversion von stream.sys verwenden, bestätigt die Streamklasse diese beiden Fälle und hält an, wenn sie erkannt werden, wenn die Synchronisierung aktiviert ist.
Wenn der Minidriver ein Filter ohne zugeordnete Hardware ist. Ein solcher Minidriver sollte bei PASSIVE_LEVEL ausgeführt werden, da keine zugrunde liegende Hardware für die Synchronisierung vorhanden ist und der Minidriver in der Regel viele Verarbeitungen ausführt. In diesem Fall ist es einfacher, eine eigene Synchronisierung durchzuführen, als den Aufwand mithilfe der Streamklassensynchronisierung zu verschwenden. Verwenden Sie bei Bedarf Mutexes, um Ihre Warteschlangen zu schützen.
Fehler im Synchronisierungscode können häufig schwer zu finden sein, und in bestimmten Umgebungen (z. B. NT-basierte Betriebssysteme, die auf Multiprozessorsystemen ausgeführt werden) können Fehler erst nach vielen Stunden Stress auftreten. Basierend auf Erfahrungen mit Anbietern sind dies nicht die Arten von Dingen, die die meisten Anbieter haben die Fähigkeit oder den Wunsch zu debuggen. Nur Treiberautoren, die mit dem Schreiben vollständig asynchroner WDM-Gerätetreiber vertraut sind, sollten versuchen, ihre eigene Synchronisierung durchzuführen.
Wenn der Minidriver ein Bus-On-Bus-Typtreiber (z. B. ein USB- oder 1394-Peripherietreiber) ist, der sich nicht wirklich um die Synchronisierung der tatsächlichen Hardware sorgt, sondern nur Anforderungen an die nächste Ebene bei PASSIVE_LEVEL aufruft und Rückrufe in der Regel bei DISPATCH_LEVEL empfängt.