StartIo-Routinen in Lowest-Level-Treibern
Der Anruf des E/A-Managers an die Dispatchroutine eines Fahrers ist die erste Phase bei der Erfüllung einer Geräte-E/A-Anforderung. Die StartIo-Routine ist die zweite Phase. Jeder Gerätetreiber mit einer StartIo-Routine ruft wahrscheinlich IoStartPacket aus seinen DispatchRead - und DispatchWrite-Routinen auf, und normalerweise für eine Teilmenge der E/A-Kontrollcodes, die er in seiner DispatchDeviceControl-Routine unterstützt. Die IoStartPacket-Routine fügt den IRP der vom System bereitgestellten Gerätewarteschlange des Geräts hinzu oder ruft sofort die StartIo-Routine des Treibers auf, um das IRP zu verarbeiten, wenn die Warteschlange leer ist.
Sie können davon ausgehen, dass das Zielgerät nicht ausgelastet ist, wenn die StartIo-Routine eines Treibers aufgerufen wird. Dies liegt daran, dass der E/A-Manager StartIo unter zwei Umständen aufruft. Entweder hat eine der Dispatchroutinen des Treibers gerade IoStartPacket aufgerufen, und die Gerätewarteschlange war leer, oder die DpcForIsr-Routine des Treibers führt eine weitere Anforderung aus und hat gerade IoStartNextPacket aufgerufen, um die nächste IRP aus der Warteschlange zu entfernen.
Bevor die StartIo-Routine in einem Gerätetreiber der obersten Ebene aufgerufen wird, sollte die Dispatchroutine dieses Treibers den Benutzerpuffer überprüft und ggf. gesperrt haben, um gültige zugeordnete Pufferadressen in der IRP einzurichten, die für die StartIo-Routine in der Warteschlange steht. Wenn ein Gerätetreiber der obersten Ebene seine Geräteobjekte für direkte E/A-Vorgänge (oder weder für gepufferte noch direkte E/A) einrichtet, kann der Treiber die Sperrung eines Benutzerpuffers nicht auf seine StartIo-Routine zurückstellen. Jede StartIo-Routine wird in einem beliebigen Threadkontext unter IRQL = DISPATCH_LEVEL aufgerufen.
Hinweis
Jeder Pufferspeicher, auf den von der StartIo-Routine eines Treibers zugegriffen werden soll, muss gesperrt oder aus dem systeminternen Speicher zugeordnet werden und muss in einem beliebigen Threadkontext zugänglich sein.
Im Allgemeinen ist jede StartIo-Routine des Gerätetreibers auf niedrigerer Ebene dafür verantwortlich, IoGetCurrentIrpStackLocation mit dem Eingabe-IRP aufzurufen und dann die anforderungsspezifische Verarbeitung auszuführen, die erforderlich ist, um den E/A-Vorgang auf dem Gerät zu starten. Die anforderungsspezifische Verarbeitung kann Folgendes umfassen:
Einrichten oder Aktualisieren von Statusinformationen zur aktuellen Anforderung, die der Treiber verwaltet. Die Zustandsinformationen können in der Geräteerweiterung des Zielgeräteobjekts oder an anderer Stelle im nicht ausgelagerten Pool gespeichert werden, der vom Treiber zugewiesen wird.
Wenn ein Gerätetreiber beispielsweise einen InterruptExpected Boolean für den aktuellen Übertragungsvorgang verwaltet, kann seine StartIo-Routine diese Variable auf TRUE festlegen. Wenn der Treiber einen Timeoutzähler für den aktuellen Vorgang verwaltet, kann seine StartIo-Routine diesen Wert einrichten, oder die StartIo-Routine könnte die CustomTimerDpc-Routine des Treibers in die Warteschlange stellen.
Wenn die StartIo-Routine den Zugriff auf Zustandsinformationen oder Hardwareressourcen für andere Treiberroutinen freigibt, müssen die Zustandsinformationen oder die Ressource durch eine Drehsperre geschützt werden. (Siehe Spin-Sperren.)
Wenn die StartIo-Routine den Zugriff auf Zustandsinformationen oder Ressourcen für die InterruptService-Routine des Treibers freigibt, muss StartIoKeSynchronizeExecution verwenden, um eine SynchCritSection-Routine aufzurufen, die auf die Status- oder Ressourceninformationen zugreift. (Weitere Informationen finden Sie unter Verwenden kritischer Abschnitte.)
Zuweisen einer Sequenznummer zum IRP für den Fall, dass der Treiber während der Verarbeitung des IRP einen Geräte-E/A-Fehler protokollieren muss.
Weitere Informationen finden Sie unter Protokollierungsfehler .
Übersetzen Sie ggf. die Parameter am E/A-Stapelspeicherort des Treibers in gerätespezifische Werte.
Beispielsweise muss ein Datenträgertreiber möglicherweise den Anfangssektor oder byteoffset auf die Adresse des physischen Datenträgers für einen Übertragungsvorgang berechnen und angeben, ob die angeforderte Länge der Übertragung eine bestimmte Sektorgrenze überschreitet oder die Übertragungskapazität des physischen Geräts überschreitet.
Wenn der Treiber ein Wechselmediengerät steuert, überprüfen Sie vor der Programmierung des Geräts für E/A auf Medienänderungen, und benachrichtigen Sie das überlastende Dateisystem, wenn sich die Medien geändert haben.
Weitere Informationen finden Sie unter Unterstützen von Wechselmedien.
Wenn das Gerät DMA verwendet, sollten Sie überprüfen, ob die angeforderte Länge (Anzahl der zu übertragenden Bytes, die sich im I/O-Stapel des IRP des Treibers befindet) in Teilübertragungsvorgänge aufgeteilt werden sollte, wie unter Eingabe-/Ausgabetechniken erläutert, wobei davon ausgegangen wird, dass ein eng gekoppelter Treiber auf höherer Ebene keine großen Übertragungen für den Gerätetreiber vorgibt.
Die StartIo-Routine eines solchen Gerätetreibers kann auch für den Aufruf von KeFlushIoBuffers und, wenn der Treiber paketbasiertes DMA verwendet, für den Aufruf von AllocateAdapterChannel mit der AdapterControl-Routine des Treibers verantwortlich sein.
Weitere Details finden Sie unter Adapterobjekte und DMA und Verwalten der Cachekohärenz.
Wenn das Gerät PIO verwendet, zuordnen Sie die unter IRP unter Irp-MdlAddress> beschriebene virtuelle Basisadresse des Puffers einer Systemraumadresse mit MmGetSystemAddressForMdlSafe.
Bei Leseanforderungen kann die StartIo-Routine des Gerätetreibers für den Aufruf von KeFlushIoBuffers verantwortlich sein, bevor die PIO-Vorgänge beginnen. Weitere Informationen finden Sie unter Verwalten der Cachekohärenz .
Wenn ein Nicht-WDM-Treiber ein Controllerobjekt verwendet, rufen Sie IoAllocateController auf, um seine ControllerControl-Routine zu registrieren.
Wenn der Treiber abbrechbare IRPs verarbeitet, überprüfen Sie, ob die Eingabe-IRP bereits abgebrochen wurde.
Wenn ein Eingabe-IRP abgebrochen werden kann, bevor er bis zum Abschluss verarbeitet wird, muss die StartIo-RoutineIoSetCancelRoutine mit dem IRP und dem Einstiegspunkt der Cancel-Routine des Treibers aufrufen. Die StartIo-Routine muss die Abbruch-Spin-Sperre für ihren Aufruf von IoSetCancelRoutine abrufen. Alternativ kann ein Treiber IoSetStartIoAttributes verwenden, um das NonCancelable-Attribut für die StartIo-Routine auf TRUE festzulegen. Dadurch wird verhindert, dass das System versucht, ein IRP abzubrechen, das durch einen Aufruf von IoStartPacket an StartIo übergeben wurde.
In der Regel verfügt ein Treiber, der gepufferte E/A verwendet, über eine einfachere StartIo-Routine als eine, die direkte E/A verwendet. Treiber, die gepufferte E/A verwenden, übertragen kleine Datenmengen für jede Übertragungsanforderung, während diejenigen, die direkte E/A verwenden (DMA oder PIO), große Datenmengen an oder aus gesperrten Puffern übertragen, die physische Seitengrenzen im Systemspeicher überspannen können.
Treiber auf höherer Ebene, die über physische Gerätetreibern liegen, richten ihre Geräteobjekte in der Regel so ein, dass sie denen ihrer jeweiligen Gerätetreiber entsprechen. Ein Treiber der obersten Ebene, insbesondere ein Dateisystemtreiber, kann Geräteobjekte jedoch weder für direkte noch gepufferte E/A-Vorgänge einrichten.
Treiber, die ihre Geräteobjekte für gepufferte E/A-Vorgänge einrichten, können sich darauf verlassen, dass der E/A-Manager gültige Puffer in allen IRPs übergibt, die an den Treiber gesendet werden. Treiber auf niedrigerer Ebene, die Geräteobjekte für direkte E/A einrichten, können sich auf den Treiber der höchsten Ebene in ihrer Kette verlassen, um gültige Puffer in allen IRPs zu übergeben, die über zwischengeschaltete Treiber an den zugrunde liegenden Gerätetreiber der niedrigeren Ebene gesendet werden.
Verwenden von gepufferten E/A-Vorgängen in StartIo-Routinen
Wenn die DispatchRead-, DispatchWrite- oder DispatchDeviceControl-Routine eines Treibers feststellt, dass eine Anforderung gültig ist und IoStartPacket aufruft, ruft der E/A-Manager die StartIo-Routine des Treibers auf, um die IRP sofort zu verarbeiten, wenn die Gerätewarteschlange leer ist. Wenn die Warteschlange nicht leer ist, wird das IRP von IoStartPacket in die Warteschlange gestellt. Schließlich bewirkt ein Aufruf von IoStartNextPacket von der DpcForIsr - oder CustomDpc-Routine des Treibers, dass der E/A-Manager die IRP aus der Warteschlange entfernt und die StartIo-Routine des Treibers aufruft.
Die StartIo-Routine ruft IoGetCurrentIrpStackLocation auf und bestimmt, welcher Vorgang ausgeführt werden muss, um die Anforderung zu erfüllen. Es verarbeitet das IRP in beliebiger Weise vor der Programmierung des physischen Geräts, um die E/A-Anforderung auszuführen.
Wenn der Zugriff auf das physische Gerät (oder die Geräteerweiterung) mit einer InterruptService-Routine synchronisiert werden muss, muss die StartIo-Routine eine SynchCritSection-Routine aufrufen, um die erforderliche Geräteprogrammierung durchzuführen. Weitere Informationen finden Sie unter Verwenden kritischer Abschnitte.
Ein physischer Gerätetreiber, der gepufferte E/A verwendet, überträgt Daten entweder an oder aus einem vom E/A-Manager zugeordneten Systemspeicherpuffer, den der Treiber in jedem IRP unter Irp-AssociatedIrp.SystemBuffer> findet.
Verwenden von direkten E/A-Vorgängen in StartIo-Routinen
Wenn die DispatchRead-, DispatchWrite- oder DispatchDeviceControl-Routine eines Treibers feststellt, dass eine Anforderung gültig ist und IoStartPacket aufruft, ruft der E/A-Manager die StartIo-Routine des Treibers auf, um die IRP sofort zu verarbeiten, wenn die Gerätewarteschlange leer ist. Wenn die Warteschlange nicht leer ist, wird das IRP von IoStartPacket in die Warteschlange gestellt. Schließlich bewirkt ein Aufruf von IoStartNextPacket von der DpcForIsr - oder CustomDpc-Routine des Treibers, dass der E/A-Manager die IRP aus der Warteschlange entfernt und die StartIo-Routine des Treibers aufruft.
Die StartIo-Routine ruft IoGetCurrentIrpStackLocation auf und bestimmt, welcher Vorgang ausgeführt werden muss, um die Anforderung zu erfüllen. Er verarbeitet den IRP in beliebiger Weise vor, z. B. das Aufteilen einer großen DMA-Übertragungsanforderung in Teilübertragungsbereiche und das Speichern des Zustands über die Länge einer eingehenden Übertragungsanforderung, die aufgeteilt werden muss. Anschließend programmiert es das physische Gerät, um die E/A-Anforderung auszuführen.
Wenn der Zugriff auf das physische Gerät (oder die Geräteerweiterung) mit der ISR des Treibers synchronisiert werden muss, muss die StartIo-Routine eine vom Treiber bereitgestellte SynchCritSection-Routine verwenden, um die erforderliche Programmierung auszuführen. Weitere Informationen finden Sie unter Verwenden kritischer Abschnitte.
Jeder Treiber, der direkte E/A verwendet, liest Daten in oder schreibt Daten aus einem gesperrten Puffer, der durch eine Speicherdeskriptorliste (MDL) beschrieben wird, den der Treiber im IRP unter Irp-MdlAddress> findet. Ein solcher Treiber verwendet häufig gepufferte E/A-Vorgänge für Gerätesteuerungsanforderungen. Weitere Informationen finden Sie unter Behandeln von E/A-Steuerungsanforderungen in StartIo-Routinen.
Der MDL-Typ ist ein undurchsichtiger Typ, auf den Treiber nicht direkt zugreifen. Stattdessen können Treiber, die PIO verwenden, Benutzerraumpuffer neu zuordnen, indem Sie MmGetSystemAddressForMdlSafe mit Irp-MdlAddress> als Parameter aufrufen. Treiber, die DMA verwenden, übergeben auch Irp-MdlAddress>, um Routinen während ihrer Übertragungsvorgänge zu unterstützen, damit die Pufferadressen logischen Bereichen für ihre Geräte neu zugeordnet werden.
Wenn ein eng gekoppelter Treiber auf höherer Ebene große DMA-Übertragungsanforderungen für den zugrunde liegenden Gerätetreiber nicht aufteilt, muss die StartIo-Routine eines Gerätetreibers der niedrigsten Ebene jede Übertragungsanforderung, die größer ist, als sein Gerät verwalten kann, in einem einzigen Übertragungsvorgang aufteilen. Treiber, die System-DMA verwenden, müssen Übertragungsanforderungen aufteilen, die für den DMA-Controller des Systems oder für ihre Geräte in einem einzigen Übertragungsvorgang zu groß sind.
Wenn es sich bei dem Gerät um ein untergeordnetes DMA-Gerät handelt, muss sein Treiber Übertragungen über einen DMA-Systemcontroller mit einem vom Treiber zugewiesenen Adapterobjekt, das den DMA-Kanal darstellt, und einer vom Treiber bereitgestellten AdapterControl-Routine synchronisieren. Der Treiber eines Bus-master DMA-Geräts muss auch ein vom Treiber zugewiesenes Adapterobjekt verwenden, um seine Übertragungen zu synchronisieren, und eine AdapterControl-Routine bereitstellen, wenn er die paketbasierte DMA-Unterstützung des Systems verwendet, oder eine AdapterListControl-Routine, wenn die Scatter-/Gather-Unterstützung des Systems verwendet wird.
Abhängig vom Design des Treibers kann er Übertragungs- und Gerätesteuerungsvorgänge auf einem physischen Gerät mit einem Controllerobjekt synchronisieren und eine ControllerControl-Routine bereitstellen.
Weitere Informationen finden Sie unter Adapterobjekte und DMA - und Controllerobjekte .
Behandeln von E/A-Steuerungsanforderungen in StartIo-Routinen
Im Allgemeinen wird nur eine Teilmenge der Geräte-E/A-Steuerungsanforderungen von der DispatchDeviceControl - oder DispatchInternalDeviceControl-Routine eines Treibers zur weiteren Verarbeitung durch die StartIo-Routine des Treibers übergeben. Die StartIo-Routine des Treibers muss nur gültige Gerätesteuerungsanforderungen verarbeiten, die Änderungen des Gerätezustands erfordern, oder flüchtige Informationen über den aktuellen Gerätestatus zurückgeben.
Jeder neue Treiber muss den gleichen Satz von öffentlichen E/A-Steuercodes unterstützen wie alle anderen Treiber für denselben Gerätetyp. Das System definiert öffentliche gerätetypspezifische E/A-Steuerungscodes für IRP_MJ_DEVICE_CONTROL Anforderungen als gepufferte Anforderungen.
Folglich führen Treiber physischer Geräte Datenübertragungen an oder aus einem Systemspeicherpuffer durch, den jeder Treiber im IRP unter Irp-AssociatedIrp.SystemBuffer> für Gerätesteuerungsanforderungen findet. Selbst Treiber, die ihre Geräteobjekte für direkte E/A einrichten, verwenden gepufferte E/A, um Gerätesteuerungsanforderungen mit öffentlichen E/A-Steuercodes zu erfüllen.
Die Definition jedes E/A-Kontrollcodes bestimmt, ob die für diese Anforderung übertragenen Daten gepuffert werden. Alle privat definierten E/A-Steuerungscodes für treiberspezifische IRP_MJ_INTERNAL_DEVICE_CONTROL Anforderungen zwischen treiberpaaren Treibern können einen Code mit einer gepufferten Methode, einer direkten Methode oder keiner Methode definieren. In der Regel sollte jeder privat definierte E/A-Steuerungscode weder mit der -Methode definiert werden, wenn ein eng gekoppelter Treiber auf höherer Ebene einen Puffer für diese Anforderung zuordnen muss.
Programmieren des Geräts für E/A-Vorgänge
In der Regel muss die StartIo-Routine in einem Gerätetreiber der niedrigsten Ebene den Zugriff auf einen beliebigen Speicher synchronisieren, oder das Gerät registriert, das es mit dem ISR des Treibers teilt, indem KeSynchronizeExecution verwendet wird, um eine vom Treiber bereitgestellte SynchCritSection-Routine aufzurufen. Die StartIo-Routine des Treibers verwendet die SynchCritSection-Routine , um das physische Gerät für E/A bei DIRQL zu programmieren. Weitere Informationen finden Sie unter Verwenden kritischer Abschnitte.
Vor dem Aufrufen von KeSynchronizeExecution muss die StartIo-Routine alle für die Anforderung erforderlichen Vorverarbeitungen durchführen. Die Vorverarbeitung kann das Berechnen eines anfänglichen Teilübertragungsbereichs und das Speichern von Zustandsinformationen zur ursprünglichen Anforderung für andere Treiberroutinen umfassen.
Wenn ein Gerätetreiber DMA verwendet, ruft seine StartIo-Routine in der Regel AllocateAdapterChannel mit einer vom Treiber bereitgestellten AdapterControl-Routine auf. Unter diesen Umständen verschiebt die StartIo-Routine die Verantwortung für die Programmierung des physischen Geräts auf die AdapterControl-Routine . Es kann wiederum KeSynchronizeExecution aufrufen, um eine vom Treiber bereitgestellte SynchCritSection-Routine das Gerät für eine DMA-Übertragung zu programmieren.