Verwenden von gepufferten E/A-Vorgängen
Ein Treiber, der ein interaktives oder langsames Gerät oder ein Gerät, das in der Regel relativ kleine Datenmengen gleichzeitig überträgt, sollte die gepufferte E/A-Übertragungsmethode verwenden. Die Verwendung von gepufferten E/A-Vorgängen für kleine, interaktive Übertragungen verbessert die Gesamtauslastung des physischen Arbeitsspeichers, da der Speicher-Manager keine vollständige physische Seite für jede Übertragung sperren muss, wie dies bei Treibern der Fall ist, die direkte E/A anfordern. Im Allgemeinen fordern Video-, Tastatur-, Maus-, serielle und parallele Treiber gepufferte E/A-Vorgänge an.
Der E/A-Manager ermittelt, dass ein E/A-Vorgang gepufferte E/A-Vorgänge wie folgt verwendet:
Für IRP_MJ_READ - und IRP_MJ_WRITE-Anforderungen wird DO_BUFFERED_IO im Flags-Element der DEVICE_OBJECT-Struktur festgelegt. Weitere Informationen finden Sie unter Initialisieren eines Geräteobjekts.
Für IRP_MJ_DEVICE_CONTROL - und IRP_MJ_INTERNAL_DEVICE_CONTROL-Anforderungen enthält der Wert des IOCTL-Codes METHOD_BUFFERED als TransferType-Wert im IOCTL-Wert. Weitere Informationen finden Sie unter Definieren von E/A-Steuerungscodes.
Die folgende Abbildung veranschaulicht, wie der E/A-Manager eine IRP_MJ_READ Anforderung für einen Übertragungsvorgang einrichtet, der gepufferte E/A verwendet.
Die Abbildung zeigt eine Übersicht darüber, wie Treiber den SystemBuffer-Zeiger im IRP verwenden können, um Daten für eine Leseanforderung zu übertragen, wenn ein Treiber die Flags des Geräteobjekts mit DO_BUFFERED_IO OReded hat:
Ein Bereich von virtuellen Benutzerspeicheradressen stellt den Puffer des aktuellen Threads dar, und der Inhalt dieses Puffers kann irgendwo innerhalb eines Bereichs von seitenbasierten physischen Adressen gespeichert werden (dunkle Schattierung in der vorherigen Abbildung).
Der E/A-Manager verarbeitet die Leseanforderung des aktuellen Threads, für die der Thread einen Bereich von virtuellen Benutzerraumadressen übergibt, die einen Puffer darstellen.
Der E/A-Manager überprüft den vom Benutzer bereitgestellten Puffer auf Barrierefreiheit und ruft ExAllocatePoolWithTag auf, um einen systembasierten Puffer (SystemBuffer) mit der Größe des vom Benutzer bereitgestellten Puffers zu erstellen.
Der E/A-Manager bietet Zugriff auf den neu zugeordneten SystemBuffer in der IRP, die er an den Treiber sendet.
Wenn die Abbildung eine Schreibanforderung zeigt, kopiert der E/A-Manager Daten aus dem Benutzerpuffer in den Systempuffer, bevor er den IRP an den Treiber sendet.
Für die in der vorherigen Abbildung gezeigte Leseanforderung liest der Treiber Daten vom Gerät in den Systemspeicherpuffer. Der Arbeitsspeicher für diesen Puffer ist nicht aus der Seite, und der Treiber kann sicher auf den Puffer zugreifen, ohne ihn zuvor zu sperren. Wenn die Leseanforderung erfüllt wurde, ruft der Treiber IoCompleteRequest mit dem IRP auf.
Wenn der ursprüngliche Thread wieder aktiv ist, kopiert der E/A-Manager die eingelesenen Daten aus dem Systempuffer in den Benutzerpuffer. Außerdem wird ExFreePool aufgerufen, um den Systempuffer freizugeben.
Nachdem der E/A-Manager einen Systemspeicherpuffer für den Treiber erstellt hat, kann der anfordernde Benutzermodusthread ausgetauscht werden, und sein physischer Arbeitsspeicher kann von einem anderen Thread wiederverwendet werden, möglicherweise durch einen Thread, der zu einem anderen Prozess gehört. Der im IRP angegebene virtuelle Adressbereich des Systemraums bleibt jedoch gültig, bis der Treiber IoCompleteRequest mit dem IRP aufruft.
Treiber, die große Datenmengen gleichzeitig übertragen, insbesondere Treiber, die mehrseitige Übertragungen durchführen, sollten nicht versuchen, gepufferte E/A-Vorgänge zu verwenden. Während das System ausgeführt wird, kann der nicht auslagerte Pool fragmentiert werden, sodass der E/A-Manager keine großen, zusammenhängenden Systemspeicherpuffer zuordnen kann, um IRPs für einen solchen Treiber zu senden.
In der Regel verwendet ein Treiber gepufferte E/A für einige IrPstypen, z. B. IRP_MJ_DEVICE_CONTROL Anforderungen, auch wenn er auch direkte E/A verwendet. Treiber, die direkte E/A verwenden, tun dies in der Regel nur für IRP_MJ_READ - und IRP_MJ_WRITE-Anforderungen und möglicherweise für treiberdefinierte IRP_MJ_INTERNAL_DEVICE_CONTROL Anforderungen, die große Datenübertragungen erfordern.
Jede IRP_MJ_DEVICE_CONTROL - und IRP_MJ_INTERNAL_DEVICE_CONTROL-Anforderung enthält einen E/A-Steuerungscode. Wenn der E/A-Steuerungscode angibt, dass die IRP mithilfe gepufferter E/A-Vorgänge unterstützt werden muss, verwendet der E/A-Manager einen einzelnen Systempuffer, um die Eingabe- und Ausgabepuffer der Benutzeranwendung darzustellen. Ein Treiber, der einen solchen E/A-Steuerungscode unterstützt, muss Eingabedaten (sofern vorhanden) aus dem Puffer lesen und dann Ausgabedaten (falls vorhanden) bereitstellen, indem die Eingabedaten überschrieben werden. Weitere Informationen finden Sie unter Definieren von E/A-Steuerungscodes.