3 Interne Strukturen und Abläufe in Windows NT

 

Nachdem die grundlegenden Konzepte von Windows NT im vorherigen Kapitel kurz genannt wurden, sollen nun einige näher betrachtet werden, die für das Verständnis bei der Programmierung von Treibern eine wesentliche Rolle spielen. So werden im folgenden Kapitel die Ausnahmebehandlung, das Interrupt Konzept und die I/O Verarbeitung unter Windows NT beschrieben. Zuerst wird aber kurz erläutert, unter welchen Umständen Programmcode im Kernelmodus ausgeführt wird.

 

3.1 Kernelmodus zurück nach oben nächster Abschnitt Inhaltsverzeichnis

In [Baker97] wird festgestellt, daß es unter Windows NT drei verschiedene Situationen gibt, in denen Code im Kernelmodus ausgeführt wird:

Hard- oder Softwareausnahmen können von Threads, die im Usermodus laufen, erzeugt werden. Ausnahmen sind eindeutig, d.h. wenn das gleiche Programm mit den gleichen Daten in der gleichen Umgebung ausgeführt wird, wird die gleiche Ausnahme eintreten. Der zur Zeit aktive Thread ist also immer die Ursache einer Ausnahme.

Interrupts sind entweder asynchrone externe Ereignisse, die von der Hardware eines Rechners erzeugt werden, oder sie werden synchron von der Software ausgelöst. Hardwareinterrupts können zu jedem beliebigen Zeitpunkt auftreten und sind nicht vorhersehbar.

Einige Treiber benutzen Kernel Mode Threads, um während Wartezeiten das System nicht zu blockieren. Wartezeiten können bei sehr langsamen Geräten oder bei Geräten, die keine Interrupts erzeugen, auftreten.

 

Im Fall einer Ausnahme oder eines Interrupts wird unter Windows NT ein Modul mit dem Namen Trap Handler aktiviert. Der Trap Handler ruft wiederum, je nach Art des Ereignisses, den Interrupt Dispatcher oder den Exception Dispatcher auf.

 

3.2 Ausnahmen (Exceptions) zurück nach oben nächster Abschnitt Inhaltsverzeichnis

Unter Windows NT werden Ausnahmen mit der sogenannten strukturierten Ausnahmebehandlung (Structured Exception Handling – SEH) verarbeitet. Dabei wird ein definierter Code-Abschnitt durch einen oder mehrere Exception Handler geschützt. Windows Programmierer sind mit diesem Konstrukt vertraut:

try {
        [...]
        geschuetzter Codeabschnitt
        [...]
        try {
                [...]
                geschuetzter Codeabschnitt mit
                neuem Handler
                [...]
            }
        except (Filter1)
            {
                Exception Behandlung
                Filter1 gibt an, welche Exceptions
                behandelt werden sollen
            }    
    }
except (Filter2)
    {
        Exception Behandlung
        Filter2 gibt an, welche Exceptions
        behandelt werden sollen
    }    

 

Bei jedem Prozedur- oder Funktionsaufruf wird ein sogenanntes Stack Frame eingerichtet. Mit dem Stack-Frame verknüpft sind ein oder mehrere Exception Handler (frame based execption handlers). Diese Handler sind ähnlich wie Prozeduraufrufe ineinander verschachtelt. Der innerste ist zuerst aktiv. Wenn er die Ausnahme behandeln konnte, geht die Kontrolle zurück an die aktuelle Position im Programm, ansonsten wird der nächste Handler aktiviert. An letzter Stelle in der Kette steht das Betriebssystem, das den aktuellen Prozeß normalerweise beendet und eine Fehlermeldung der Art "Unhandled Exception ..." ausgibt, wenn die Ausnahme nicht vorher abgefangen wird.

Damit das Betriebssystem in jedem Fall die Ausnahme behandeln kann, wird für jeden Thread eine Behandlungsroutine deklariert. Dies erfolgt laut [Solom98] über die beiden internen Funktionen Win32StartOfProcess und Win32StartOfThread, die aufgerufen werden, wenn ein Programm gestartet wird bzw. wenn ein Programm neue Threads erzeugt. In [Solom98] wird der generische Code für die Win32StartOfProcess wie folgt angegeben:

void Win32StartOfProcess(LPVOID lpvThreadParm)
{
        __try
                {
                 DWORD dwThreadExitCode = lpStartAddr(lpvThreadParm);
                 ExitThread(dwThreadExitCode);
                }
        __except(UnhandledExceptionFilter(GetExcepetionInformation()))
                {
                 ExitProcess(GetExecptionCode());
                }
}

 

In der Registry kann unter "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\AeDebug" eingestellt werden, wie das Betriebssystem auf nicht behandelte Ausnahmen reagiert. So kann zum Beispiel ein Debugger aufgerufen werden, um das Problem näher zu untersuchen. Genaue Informationen dazu findet man in [Registry97] unter dem Begriff "Debugger".

Tritt eine Ausnahme auf, wird von der CPU der Trap Handler aktiviert. Dieser erzeugt ein sogenanntes Trap Frame, das es dem System erlaubt, das gegenwärtige Programm dort fortzusetzen, wo es unterbrochen wurde. Außerdem wird eine Struktur erzeugt, die unter anderem Informationen über die Ursache der Ausnahme enthält. Danach wird der Exception Dispatcher aufgerufen. Dieser muß nun die passende Behandlungsroutine für die Ausnahme finden und zur Ausführung bringen. Dabei wird unterschieden, ob die Ausnahme im Kernelmodus oder im Usermodus auftrat. Im ersten Fall wird nur eine Routine aufgerufen, um eine Behandlungsroutine zu finden und diese zu aktivieren.

Trat die Ausnahme im Benutzermodus auf, überprüft der Dispatcher zunächst, ob der aktuelle Prozeß mit einem Debugger-Prozeß verknüpft ist. Wenn dies so ist, sendet er eine sogenannte First Chance Nachricht an den Debugger. Sollte der Debugger die Ausnahme nicht behandeln oder der Prozeß nicht mit einem Debugger verbunden sein, ruft der Dispatcher eine Routine auf, um einen framebasierten Exception Handler zu finden. Scheitert die Routine, weil sie keinen Handler findet bzw. die Ausnahme nicht verarbeitet wurde, wird noch einmal eine Nachricht an den Debugger geschickt, sofern dieser vorhanden ist. Reagiert der Debugger auf die sogenannte Second Chance Nachricht nicht oder ist er nicht vorhanden, wird zunächst der Exception Handler des Untersystems aufgerufen. Sollte dieser ebenfalls die Ausnahme nicht behandeln, wird schließlich der Standardhandler des Betriebssystems aktiviert. Die Abbildung 3.2.5-1 verdeutlicht noch einmal den Ablauf.

Weitere Informationen zur Ausnahmebehandlung findet man in [Solom98] und in [Lauer96].

Abbildung 3.2.5-1: Ablauf der Ausnahmebehandlung

 

 

3.3 Interrupts zurück nach oben nächster Abschnitt Inhaltsverzeichnis

Man unterscheidet prinzipiell zwischen zwei Arten von Interrupts. Zum einen Hardware Interrupts, die von angeschlossenen Geräten ausgelöst werden, und zum anderen Software Interrupts, die von einem Programm oder vom Betriebssystem aufgerufen werden. Wenn ein Gerät dem Prozessor ein Ereignis mitteilen möchte, löst es einen Hardware Interrupt aus, der dann die entsprechende Behandlungsroutine startet. Software Interrupts werden unter Windows NT zum Beispiel benutzt, um einen Kontextwechsel herbeizuführen.

In den folgenden Abschnitten werden die wichtigsten Punkte des Interrupt Konzeptes unter Windows NT erläutert.(Fußnote 1)

 

3.3.1 Interrupt Request Levels – IRQLs zurück nach oben nächster Abschnitt Inhaltsverzeichnis

Interrupts können auf den meisten Hardwareplattformen unterschiedliche Prioritäten haben. Da aber die Art der Einteilung sehr verschieden ist, benutzt Windows NT ein eigenes abstraktes Modell der Priorisierung. Dabei werden die unterschiedlichen Hardware Interrupt Level durch den Interrupt Dispatcher auf eine standardisierte Menge von sogenannten Interrupt Request Levels (IRQLs) abgebildet.

Jeder Level stellt eine Zahl dar – je höher die Zahl, um so wichtiger der Interrupt. Die Anzahl der IRQLs ist vom jeweiligem Prozessor abhängig. Ist der IRQL gleich Null (auch Passive Level genannt), werden normale User Threads ausgeführt. Außer im Passive Level werden nur Interrupts ausgeführt, deren IRQL höher ist als der aktuelle. (Fußnote 2)

Die Interrupts bis zum IRQL DISPATCH_LEVEL sind Software Interrupts, alle darüber sind Hardware Interrupts. Den IRQLs Device Level werden die Interrupts der angeschlossenen Geräte zugeordnet. Wie die Tabelle 3.3.1-1 verdeutlicht, stehen auf Intel Plattformen insgesamt 24 IRQLs für Geräte zur Verfügung, auf Alpha Plattformen dagegen nur zwei. Laut [Russ97a] deutet dieser Unterschied darauf hin, daß Windows NT die allgemeinen Hardware Interrupts nicht wirklich priorisiert. Einigen speziellen Hardware Interrupts werden jedoch eigene IRQLs zugeordnet. Der Zeitgeberinterrupt erhält z.B. den IRQL CLOCK_LEVEL und der Interprozessorinterrupt, der zum Austausch von Informationen zwischen zwei CPU benutzt wird, erhält den IRQL IPI_LEVEL.

 

 

   

 

31

High

HIGH_LEVEL

   

30

Power Fail

POWER_LEVEL

   

29

Interprocessor Interrupt

IPI_LEVEL

   

28

Clock

CLOCK_LEVEL

   

27

Profile

PROFILE_LEVEL

7

High

HIGH_LEVEL

 

Device n

DIRQL

6

Interprocessor Interrupt

IPI_LEVEL

.

.

.

5

Clock

CLOCK_LEVEL

4

Device high

DIRQL

 

.

.

.

3

Device

DIRQL

 

Device 0

DIRQL

2

Dispatch/DPC

DISPATCH_LEVEL

2

Dispatch/DPC

DISPATCH_LEVEL

1

APC

APC_LEVEL

1

APC

APC_LEVEL

0

Low

PASSIV_LEVEL

LOW_LEVEL

0

Low

PASSIV_LEVEL

LOW_LEVEL

 

Alpha

 

x86

Tabelle 3.3.1-1: IRQLs (Vergleich Alpha - x86)

 

3.3.2 Das Interrupt Objekt
zurück nach oben nächster Abschnitt Inhaltsverzeichnis

Damit ein Treiber auf einen Interrupt reagieren kann, muß er eine sogenannte Interrupt Service Routine (ISR – auch Interruptbehandlungsroutine) zur Verfügung stellen und dies dem I/O Manager mitteilen. Dies geschieht mit Hilfe der IoConnectInterrupt Funktion. Durch den Aufruf dieser Funktion wird ein neues Interrupt Objekt erzeugt, das unter anderem die Einsprungadresse der Serviceroutine und Informationen über den IRQL enthält. Außerdem aktualisiert die Funktion die Interrupt Dispatch Table (IDT), über die die Zuordnung der Service Routine zu einem Interrupt erfolgt.

Beim Initialisieren des Interrupt Objektes wird auch ein sogenannter Dispatch Code im Objekt gespeichert. Dieser Code wird als erstes aufgerufen, wenn der Interrupt ausgelöst wird und ruft seinerseits die interne Kernelfunktion KiInterruptDispatch auf.

 

3.3.3 Interruptbehandlung zurück nach oben nächster Abschnitt Inhaltsverzeichnis

Da die Interruptbehandlung unter Windows NT nicht exakt dokumentiert ist, wird auch in der Literatur das Thema recht vage behandelt. Vereinfacht gesehen passiert folgendes:

  1. Der Interrupt tritt auf.
  2. Der IRQL des Interrupts wird mit dem gegenwärtigen verglichen.
  3. Ist der eintreffende IRQL kleiner oder gleich, wird der Interrupt vorerst ignoriert und zu einem späteren Zeitpunkt abgearbeitet.
    Andernfalls werden die folgenden Schritte durchgeführt.
  4. Der aktuelle Thread wird unterbrochen.
  5. Alle Interrupts werden deaktiviert, d.h. es können keine außer NMIs (Non Maskable Interrupts – Nicht Maskierbare Interrupts) auftreten. Es werden die nötigsten Statusinformationen gesichert, um nach der Abarbeitung des Interrupts den Thread fortsetzen zu können
  6. Der Interrupt Dispatcher wird aufgerufen. Dieser erhöht den IRQL auf den Wert des eingetroffenen Interrupts. Danach werden Interrupts wieder zugelassen.
  7. Die zugehörige Interrupt Service Routine wird aufgerufen. Das Gerät wird abgefragt und der Interrupt wird bestätigt (Interrupt Acknowledge).
  8. Der Interrupt Dispatcher erhält die Kontrolle zurück und senkt den IRQL auf den ursprünglichen Wert.
  9. Der unterbrochene Thread wird fortgesetzt.

Die Reihenfolge der Punkte kann nicht exakt festgelegt werden. Da die Punkte 2 und 3 in der angegeben Reihenfolge nur durch den Interrupt Controller durchgeführt werden könnten, ist es sehr wahrscheinlich, daß Windows NT sie erst nach Punkt 4 und 5 durchführt. Wie Dale Roberts in [Roberts98] beschreibt, ist dies, zumindest auf der x86 Plattform, aber nicht immer so. In seinem Artikel stellt er fest, das Windows NT teilweise den PIC (Programmable Interrupt Controller) benutzt, um in bestimmten Situationen Hardware Interrupts auszublenden. In diesem Fall würden die Punkte 2 und 3 durch die Hardware ausgeführt werden (Fußnote 3) , während der Prozessor nicht in seiner Arbeit unterbrochen wird.

Die Bestätigung des Interrupts am Interrupt Controller wird laut Roberts im Punkt 6 durchgeführt, so daß, während die ISR läuft, weitere Interrupts eintreffen können. Dadurch kann Windows NT eigene Prioritäten bei der Abarbeitung der Interrupts festlegen.

In der Abbildung 3.3.3-1 wird die Interrupt Behandlung noch einmal verdeutlicht.

Abbildung 3.3.3-1: Interrupt Behandlung

 

 

3.3.4 IDT Look zurück nach oben nächster Abschnitt Inhaltsverzeichnis

Bei den bisher dargelegten Konzepten bleiben zwei Fragen offen, nämlich wie Windows NT seine eigenen IRQLs auf die hardwareseitig zur Verfügung gestellten abbildet und wie die Interruptvektoren in der Interrupt Descriptor Table (IDT) verteilt werden. Eine Anfrage in verschiedenen Newsgroups zu diesem Thema zeigte, daß die einhellige Meinung vorherrscht, daß Windows NT die IRQLs ebenso wie die Interruptvektoren auf jedem Rechner unterschiedlich zuordnet. Einige waren sogar der Meinung, daß Windows NT die Verteilung auf dem selben Rechner bei jedem Neustart unterschiedlich vornehmen würde. Allerdings konnte keiner den Algorithmus beschreiben, den das Betriebssystem bei der Verteilung benutzt.

Im folgenden soll ein vom Autor entwickeltes Werkzeug vorgestellt werden, das die Zuweisung der IRQLs und der Interruptvektoren im begrenzten Maße darstellen kann. Das Programm IDT Look liest auf Intel Rechnern die IDT des Prozessors aus und ordnet mit Hilfe von Registry Einträgen den Vektoren Geräte und IRQLs zu. Zu Vektoren, für die keine Informationen in der Registry gespeichert sind, können keine weiteren Angaben gemacht werden.

Sofern nichts anderes vermerkt wurde, beziehen sich die folgenden Beschreibungen auf Rechner der Intel x86 Architektur.

 

  1. Aufbau der Interrupt Descriptor Table
  2. Der Aufbau der IDT ist plattformabhängig. Bei Intel Prozessoren wird die IDT hardwareseitig implementiert. Die CPU besitzt ein spezielles Register, das IDT Register (IDTR), das die Größe und die Lage der IDT im Speicher festlegt. Die IDT selbst ist ein Feld von 8 Bytes großen Descriptoren. Da die Intel Architektur maximal 256 Interrupts unterstützt, sind auch nur maximal 256 Einträge in der IDT möglich. Es können weniger Descriptoren vorhanden sein, da für Interrupts, die nicht auftreten, keine Desciptoren benötigt werden. Die Abbildung 3.3.4-1 beschreibt den Aufbau der Descriptoren.

     

    Abbildung 3.3.4-1: IDT Descriptor und IDT Register

     

  3. Auslesen der IDT
  4. Da der Zugriff auf die IDT im Usermodus verständlicherweise untersagt ist, wurde vom Autor ein kleiner Treiber entwickelt, der das Auslesen der IDT übernimmt.

    Wie schon erwähnt, wird die Position und Größe der IDT über das IDT Register festgelegt. Der Zugriff auf dieses Register erfolgt über die Assemblerbefehle LIDT (Load IDT Register) und SIDT (Store IDT Register) (Fußnote 4) .

    Die folgende Routine liest das IDT Register aus und kopiert danach die IDT in das Feld mBuffer:

    _asm
              {
                    mov   esi,mBuffer
                    sidt  [esi]                             //IDTR nach *mBuffer
                    movzx ecx,word ptr [esi]                //Groesse nach ECX
                    mov   ebx,dword ptr [esi+2]             //Adresse nach EBX
                    inc   ecx                                
                    mov   edi,mBuffer                       //Ziel nach EDI
                    mov   dummy,ecx                         //Groesse merken
                    mov   esi,ebx                           //IDT Adr. nach ESI
                    shr   ecx,2                             //wegen movsd
                    rep   movsd                             //kopieren
              }
    

     

  5. Auswerten der Registry Einträge
  6. Im nachfolgend angegeben Registry Schlüssel findet man Informationen über die Ressourcen, die von den unterschiedlichen Geräten verwendet werden.

    Registry Schlüssel:

    HKEY_LOCAL_MACHINE\Hardware\Ressourcemap

    Hier befinden sich mehrere Schlüssel deren Unterschlüssel wiederum die gesuchten Werte vom Typ REG_RESOURCE_LIST enthalten. Dieser Datentyp ist von Microsoft nicht dokumentiert. In der Datei ntddk.k findet man jedoch die Definition der Struktur _CM_RESOURCE_LIST, die offensichtlich mit REG_RESOURCE_LIST übereinstimmt.

    Der Einfachheit halber wurde im Programm IDT Look auf die komplette Implementierung des Datentyps verzichtet. Statt dessen werden nur zwei untergeordnete Strukturen TPartialResourceDescriptor und TPartialResourceList integriert, die zudem den dynamischen Teil der Strukturen durch einen statischen ersetzen. Im Testbetrieb wurden durch diese Einschränkungen keine Probleme verursacht.

     

  7. Das Programm
  8. Nach dem Start des Programms erhält man eine Liste der Interrupts mit den Informationen aus der IDT, sowie eine Liste der den Interrupts zugeordneten Geräte (siehe Abbildung 3.3.4-2). Im unteren Abschnitt wird angezeigt, wieviele Geräte entdeckt wurden und wieviele Einträge die IDT enthält. Wenn man doppelt auf einen Listeneintrag klickt, erhält man zusätzliche Informationen über den Interrupt und über das zugeordnete Gerät, soweit diese vorhanden sind.

    Über den Menüpunkt File kann man die Informationen abspeichern und vorher gespeicherte Daten laden. Mit F5 wird die Anzeige mit der aktuellen lokalen IDT erneuert.

     

     

    Abbildung 3.3.4-2: IDT_Look

     

  9. Auswertung der Daten
  10. Das Programm wurde auf mehreren Rechnern mit sehr unterschiedlicher Hard- und Softwareausstattung ausgeführt. Die Annahme, daß Windows NT die IRQL bei jedem Start unterschiedlich zuordnet, kann nicht bestätigt werden. Alle Rechner hatten auch nach einem Neustart die gleiche IRQL Verteilung. Im Grunde scheint es zwei unterschiedliche Algorithmen zu geben, die NT bei der Zuordnung der IRQLs benutzt. Welcher Algorithmus verwendet wird, hängt davon ab, ob ein Einprozessor- oder ein Mehrprozessorsystem vorliegt und damit, welche Hardware Abstraction Layer (HAL) verwendet wird (Fußnote 5). Die Standardgeräte, wie zum Beispiel der serielle Anschluß oder das Diskettenlaufwerk, bekommen offensichtlich auf Einprozessorsystemen immer die gleichen Werte zugeordnet. Der serielle Anschluß erhält den IRQL 23, der dem Vektor 0x34 zugeordnet wird, und das Diskettenlaufwerk erhält den IRQL 21, mit dem Vektor 0x36. Bei Mehrprozessorsystemen erfolgt die Zuordnung ebenfalls gleich, aber mit anderen Levels und Vektoren.

    Im Anhang befindet sich eine Liste der getesteten Systeme und eine Tabelle mit der Zuordnung der IRQLs. Bemerkenswert ist außerdem, daß die drei Mehrprozessorsysteme offensichtlich einen IRQL mit dem Wert 255 (Vektor 0x50) benutzen, der in keiner Literatur erwähnt wird. Ebenfalls interessant ist die Tatsache, daß bei allen getesteten Rechnern die Interruptvektoren 0x2A bis 0x2E vom Usermodus aus aufgerufen werden können. (Fußnote 6)

     

3.3.5 Deferred Procedure Calls (DPCs) – Verzögerte Prozeduraufrufe zurück nach oben nächster Abschnitt Inhaltsverzeichnis

Während ein Interrupt verarbeitet wird, werden alle anderen mit niedrigeren oder gleichen IRQL ignoriert. Damit die Antwortzeiten des Systems trotzdem relativ niedrig bleiben, wird versucht, soviel Code wie nur möglich mit einem sehr niedrigen IRQL auszuführen. Mit verzögerten Prozeduraufrufen (DPCs) kann man genau dies erreichen.

Wenn eine Service Routine mit einem hohen IRQL den Rest ihrer Arbeit auch mit einem niedrigeren IRQL durchführen kann, erzeugt sie ein neues DPC Objekt, das in einer Warteschlange eingereiht wird, und ruft einen DPC Software Interrupt auf. Da der gegenwärtige IRQL höher ist, wird der Interrupt nicht sofort bearbeitet. Sobald aber der IRQL unter DISPATCH_LEVEL fällt, wird der DPC Dispatcher aufgerufen. Dieser verarbeitet nun alle anstehenden DPC Objekte in der Warteschlange. Dabei ist der IRQL der CPU gleich DISPATCH_LEVEL, d.h. die CPU kann sofort auf jeden eintreffenden Hardware Interrupt reagieren.

 

3.3.6 Asynchronous Procedure Calls (APCs) – Asynchrone Prozeduraufrufe zurück nach oben nächster Abschnitt Inhaltsverzeichnis

Mit Hilfe von APCs kann man unter Windows NT Code im Kontext eines bestimmten Threads ausführen. Das bedeutet, daß der Code im gleichen virtuellen Adreßraum wie der Thread abgearbeitet wird. APCs werden, ähnlich wie DPCs, in einer Warteschlange vom Kernel verwaltet. Im Gegensatz zur DPC Queue ist die APC Warteschlange aber nicht global, sondern thread-spezifisch, d.h. jeder Thread hat seine eigene APC Warteschlange. APCs werden also immer in die Warteschlange des Threads eingefügt, unter dessen Kontext sie später ausgeführt werden sollen.

Nachdem ein APC in die Warteschlange eingefügt wurde, wird ein Softwareinterrupt mit dem IRQL APC_LEVEL aufgerufen. Sobald der Thread gestartet wird, wird der APC ausgeführt.

Der I/O Manager benutzt beispielsweise APCs, um eine I/O Anfrage abzuschließen. Dabei werden im Kontext des Threads, der die Anfrage gestartet hat, z.B. Speicherbereiche kopiert und freigegeben oder ein Eventobjekt auf einen signalisierenden Status gesetzt.

 

3.4 I/O Verarbeitung zurück nach oben nächster Abschnitt Inhaltsverzeichnis

Wie unter Unix erfolgt die I/O Verarbeitung in Windows NT dateibasiert, d.h. daß alle Anwendungen ihre I/O Aktionen auf virtuelle Dateien ausführen. Welches Gerät oder Medium eine solche virtuelle Datei letztendlich repräsentiert, wird von einem Treiber festgelegt.

Beim Öffnen einer virtuellen Datei erhält der Aufrufer ein Handle für ein Dateiobjekt. In diesem Objekt ist unter anderem der Dateiname abgelegt, der den internen Namen eines Geräteobjekts (Fußnote 7) enthält, das für diese Datei verantwortlich ist. So lautet beispielsweise der Dateiname für die Datei test.txt auf dem Diskettenlaufwerk A: \Device\Floppy0\test.txt. Der Teil \Device\Floppy0 ist der interne Name für das Geräteobjekt, das dieses Diskettenlaufwerk repräsentiert. Der interne Name kann von Anwendungen nicht benutzt werden. Statt dessen muß der Gerätetreiber in einem speziellen Verzeichnis (Fußnote 8) \DosDevices eine symbolische Verknüpfung (symbolic link) mit dem internen Namen herstellen.

Wenn eine Anwendung eine I/O Aktion durchführt, ruft sie zunächst eine dokumentierte Funktion des Win32 API auf. Diese ruft eine interne Funktion des I/O Untersystems auf, die den Aufruf an den I/O Manager weiterleitet. Der I/O Manager entscheidet anhand des übergebenen Dateihandles, welcher Gerätetreiber die Aktion verarbeiten muß und ruft diesen anschließend auf. Der Gerätetreiber setzt letztendlich die I/O Anforderung gerätespezifisch um. Dies kann entweder direkt oder mit Hilfe von Funktionen der HAL erfolgen. Die Abbildung 3.3.6-1 verdeutlicht noch einmal den Ablauf einer I/O Aktion.

Abbildung 3.3.6-1: I/O Verarbeitung unter Windows NT

 

3.5 Treiber unter Windows NT zurück nach oben nächster Abschnitt Inhaltsverzeichnis

Im allgemeinen unterscheidet man unter Windows NT zwei grundlegende Arten von Treibern:

Diese Diplomarbeit beschäftigt sich auschließlich mit Kernelmodus Treibern die in

in [Kernel96] wiederum in drei Gruppen unterteilt werden:

Gerätetreiber greifen direkt auf die Hardware zu. Wenn sie in der untersten Schicht in einer Kette von Treibern arbeiten, werden Gerätetreiber auch lowest level Treiber genannt. Zu den Gerätetreibern gehören zum Beispiel Tastatur- und Festplattentreiber.

Diese Treiber benutzen Funktionen von Gerätetreibern, die eine oder mehrere Ebenen unter ihnen liegen. Vermittlungstreiber werden beispielsweise für virtuelle Festplatten oder für Spiegelungssoftware benutzt.

Jeder Dateisystemtreiber benutzt Funktionen des oder der ihm zugrunde liegenden Gerätetreibern, wobei einige auch zusätzlichen Gebrauch von Vermittlungstreibern machen.

Die im Kapitel 5 vorgestellten Beispieltreiber gehören zur Gruppe der Gerätetreiber.

David A. Solomon differenziert in [Solom98] die Gruppen etwas feiner. Er unterscheidet:





written 1998/1999 by Guido Wischrop
all rights reserved