4 Grundlagen zur Programmierung von Treibern

In diesem Kapitel sollen die theoretischen und praktischen Grundlagen vermittelt werden, die für die Entwicklung von Treibern notwendig sind. Neben der Entwicklungsumgebung wird zunächst beschrieben, wie Treiber übersetzt und installiert werden. Danach werden der grundlegende Aufbau eines Treibers und die wichtigsten Datenstrukturen erläutert. Anschließend werden das Debugging mit WinDbg erklärt und die Grundlagen des Eventlogging unter Windows NT dargelegt.

 

4.1 Erstellen von Treibern für Windows NT

 

4.1.1 Die Entwicklungsumgebung zurück nach oben nächster Abschnitt Inhaltsverzeichnis

Zur Entwicklung eines Treibers benötigt man im einfachsten Fall nur einen Rechner und die notwendige Software. Sobald man aber in die Verlegenheit kommt, Kerneldebugging betreiben zu müssen, bleibt keine andere Wahl, als zusätzlich einen zweiten Rechner zu verwenden, sofern man auf die Tools von Microsoft angewiesen ist.(Fußnote 9)

 

Entwicklungssystem

Zielsystem

Software

Voraussetzung:

Windows NT 4.0 retail build

MS Internet Explorer 4.0

MS Visual C++ (Standard Headerfiles, MFC, nmake)

NT Device Driver Development Kit (NT DDK)

Win 32 Software Development Kit (Win32 SDK)

Quellcode des Treibers

Empfehlung:

UltraEdit 32

Symbol Dateien vom Zielsystem

Voraussetzung:

Windows NT 4.0 retail build

Windows NT 4.0 checked build

Ausführbarer Treiber (driver executabel)

Empfehlung:

Windows NT Hardware Kompatibilitätstest (Windows NT HCT)

Crash Dump File

Hardware

Voraussetzung:

Windows NT 4.0 kompatible Hardware

Empfehlung:

Pentium 133 MHz

32 MB RAM

Netzwerkkarte

1,5 GB freier Festplattenspeicher für Entwicklungstools

Voraussetzung:

Windows NT 4.0 kompatible Hardware

Empfehlung:

Pentium 90 MHz

24 MB RAM

Netzwerkkarte

Tabelle 4.1.1-1: Systemvoraussetzungen der Entwicklungsrechner

 

In [DDK96] werden die minimalen Systemvoraussetzungen beschrieben. Die Tabelle 4.1.1-1 enthält einen Überblick über die notwendige Software und Empfehlungen für die Hardware-Ausstattung der beiden Rechner.

Auf einem der beiden Rechner, dem Entwicklungssystem, wird die zur Erstellung des Treibers benötigte Software und der Kerneldebugger installiert. Der andere Rechner, das Zielsystem, wird mit der Hardware ausgerüstet, die der Treiber ansprechen soll. Sind die beiden Rechner unterschiedlich ausgestattet, sollte man den schnelleren als Entwicklungssystem benutzen. Um das Kerneldebugging zu ermöglichen, müssen die Rechner mit einem seriellen Nullmodemkabel verbunden werden. Es empfiehlt sich weiterhin, beide Rechner in ein Netzwerk zu integrieren, damit der Austausch der Daten nicht über Disketten erfolgen muß.

Zum Bearbeiten der Treiberquelltexte kann ein ganz normaler Texteditor verwendet werden. Der Autor benutzt den Editor UltraEdit, da er verschiedene Funktionen zur Vereinfachung der Programmierung bietet, wie z.B Syntaxhervorhebung, definierbare Shortcuts und den Aufruf externer Programme.

 

4.1.2 Übersetzen des Treibers zurück nach oben nächster Abschnitt Inhaltsverzeichnis

Bevor man den Treiber kompiliert, muß man zunächst die Zielumgebung auswählen. In der Testphase verwendet man normalerweise die Checked Build Umgebung, die im Gegensatz zur Free Build Umgebung, die Debuginformationen in den ausführbaren Treiber integriert. Zur Auswahl der Umgebung benutzt man entweder die Verknüpfungen, die durch das Setup Programm des Windows NT DDK eingerichtet wurden oder man startet von der Kommandozeile das Batch setenv mit den entsprechenden Parametern free oder checked. Wird beim Start von setenv kein Parameter übergeben, wird automatisch die Free Build Umgebung ausgewählt.

Nachdem die Zielumgebung ausgewählt wurde, wechselt man in das Quellcodeverzeichnis des Treibers und startet das Programm Build.

 

    Das Build Utility

Das Programm Build übernimmt die Übersetzung des Treibers. Es löst dabei automatisch die Abhängigkeiten für die unterschiedlichen Hardwareplattformen, übernimmt den Aufruf von nmake, das die Datei MAKEFILE erstellt, und wählt die richtigen Parameter für den Compiler und den Linker. Dazu werden sogenannte Kommandofiles benutzt.

Build wertet folgende Kommandofiles im Verzeichnis \ddk\inc aus:

Standard Makefile für build (master control file)

bestimmt die Zielplattform

enthalten plattformspezifische Compiler und Linker Optionen

Zusätzlich werden im aktuellen Verzeichnis die Dateien SOURCES und MAKEFILE verarbeitet.

 

    Das SOURCES File

Der Name der Datei ist "SOURCES" ohne eine Dateiendung. Das File enthält eine Reihe von Schlüsselworten, die die Build Operation beschreiben. Die wichtigsten sind:

gibt eine Liste von Pfaden an, die die Headerfiles enthalten

Liste von Quelltextdateien

der Zielpfad

der Name des Treibers (ohne Endung)

die Endung des Treibers

legt den Typ des Ziels fest (DRIVER, GDI_DRIVER, MINIPORT, LIBRARY, DYNLINK, ...)

Bibliotheken die mit gelinkt werden

Dem Schlüsselwort muß ohne ein Leerzeichen ein "=" folgen. Zeilen die mit einem "#" beginnen, werden als Kommentarzeilen behandelt. Lange Zeilen können mit einem "\" auf der nächsten Zeile fortgesetzt werden.

 

Beispiel:

#
# SOURCE - File für den Treiber K
#
TARGETNAME=k
TARGETPATH=e:\myfiles\k
TARGETTYPE=DRIVER
INCLUDES=$(BASEDIR)\inc

SOURCES= k.c\
         k_msg.rc

 

    Logfiles von Build

Das Build Utility legt folgende drei Logfiles an:

enthält eine Liste der Kommandos, die NMAKE ausführt

enthält Warnungen, die aufgetreten sind

enthält eine Liste der Fehler, die aufgetreten sind

 

    Verzeichnisstruktur

Die Abbildung 4.1.2-1 veranschaulicht die Verzeichnisstruktur, die das Build Utility beim Übersetzen des Treibers benutzt.

Abbildung 4.1.2-1: Verzeichnisstruktur des Build Tools

 

ACHTUNG: Build erstellt zwar die Pfade für die verschiedenen Plattformen, die Pfade FREE bzw. CHECKED müssen allerdings von Hand erstellt werden, sonst bricht das Programm mit der Fehlermeldung "Datei XX kann nicht geöffnet werden" ab. In der Praxis bedeutet dies, daß für die jeweilige Zielplattform die gesamte Verzeichnisstruktur von Hand erstellt werden muß.

 

4.1.3 Installation des Treibers zurück nach oben nächster Abschnitt Inhaltsverzeichnis

Es gibt mehrere Wege, um einen Treiber zu installieren. Man kann ihn manuell installieren, die Funktionen des Advanced Windows 32 Base API verwenden oder eine INF Datei benutzen, die vom Treiberentwickler bereitgestellt wurde.

 

  1. Manuelle Installation
  2. Beim manuellen Installieren des Treibers muß man zunächst die ausführbare Datei (diese Datei hat die Endung ".SYS") in das Verzeichnis %SystemRoot%\SYSTEM32\ DRIVERS kopieren. Danach müssen einige Werte in die Registry eingetragen werden. Die Tabelle 4.1.3-1 gibt einen Überblick über die notwendigen Einträge.

     

    Name

    Datentyp

    Beschreibung

    Treibername

    (Schlüssel)

    *)

    Type

    REG_DWORD

    Art des Treibers *)

    0x1 – Kernel-Mode Treiber

    0x2 – File-System Treiber

    Start

    REG_DWORD

    Wann soll der Treiber gestartet werden: *)

    0x0 – SERVICE_BOOT_START

    Treiber wird vom OS Loader gestartet noch bevor das Betriebssystem (BS) geladen wurde.

    0x1 – SERVICE_SYSTEM_START

    Treiber wird, nachdem das BS geladen wurde, gestartet (das BS ist noch in der Initialisierungsphase).

    0x2 – SERVICE_AUTO_START

    Treiber wird nach dem vollständigem Start des BS vom Service Control Manager (SCM) gestartet

    0x3 – SERVICE_DEMAND_START

    Treiber wird manuell gestartet (über die Systemsteuerung oder über WIN32 API Aufrufe)

    0x4 – SERVICE_DISABLED

    Treiber kann nicht gestartet werden, bis der Registry-Eintrag Start einen andern Wert bekommt.

    ErrorControl

    REG_DWORD

    Reaktion vom System, wenn der Treiber nicht gestartet werden kann. *)

    0x0 – Fehler im Log eintragen und ignorieren

    0x1 – Fehler im Log eintragen und eine Meldung

    anzeigen

    0x2 – Fehler im Log eintragen und mit der letzten

    bekannten funktionierenden Konfiguration neu starten

    0x3 – Fehler im Log eintragen und System

    stoppen, falls die letzte bekannte funktionierende Konfiguration schon aktiv ist

    Group

    REG_SZ

    Gruppe des Treibers

    DependOnGroup

    REG_MULTI_SZ

    andere Treiber, die von diesem Treiber benötigt werden

    Tag

    REG_BINARY

    Treiber soll in Abhängigkeit der Reihenfolge in einer Gruppe geladen werden

    Parameters

    (Schlüssel)

    treiberspezifische Parameter

    *) - Eintrag wird benötigt

    Tabelle 4.1.3-1: Registry Einträge

     

    Die Einträge müssen in folgendem Zweig in der Registry gesetzt werden:

    HKEY_LOCAL_MACHINE
    ï
    ï - SYSTEM
    	ï
            ï - CurrentControlSet
    	        ï
                    ï - Services
    	                ï
    	                ï - Treibername
    		                ï
    		                ï - ErrorControl
    		                ï - Start
    		                ï - Type
    		                ï
    		                ï - Parameters
                    			ï
    			                ï - (Parameter des Treibers)
    			                ï - 		:
    
    

     

    Die Erstellung der Einträge in der Registry kann auch über Skripte erfolgen. Ein solches Skript hat die Dateiendung ".REG". In dem Skript wird als erstes der Schlüssel mit kompletten Pfad in eckigen Klammern angegeben. Danach folgen die Werte, die der Schlüssel enthalten soll, mit dem Format:

    "Wertname" = Datentyp:Wert

    Bei Werten vom Typ REG_SZ oder REG_MULTI_SZ muß der Datentyp nicht angegeben werden. In einem Skript können mehrere Schlüssel verarbeitet werden.

    Beispielscript zum Erstellen der Einträge (Datei k.reg):

    [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\k]
    "ErrorControl" = dword:00000001
    "Type" = dword:00000001
    "Start" = dword:00000001
    "Group" = "Keyboard Class"
    "DisplayName"  = "K"
    

     

    Nachdem die Werte in die Registry eingetragen wurden, muß der Rechner neu gestartet werden. Abhängig vom Wert Start wird der Treiber entweder nach dem nächsten Start geladen oder man kann ihn in der Systemsteuerung über den Eintrag Geräte manuell starten. Dort kann man auch den Treiber entladen, sofern er dies unterstützt, und die Startoptionen ändern.

     

  3. Installation mit Hilfe des Advanced Windows 32 Base API
  4. Der Service Control Manager (SCM) stellt über das Advanced Windows 32 Base API alle Funktionen zum Installieren, Laden und Entladen sowie zum Starten und Beenden eines Treibers zur Verfügung.

    Im folgenden werden die wichtigsten Funktionen kurz beschrieben:

    Eine komplette Beschreibung aller Funktionen des SCM findet man in [Visual98] unter "Plattform-SDK/Windows Base Services/Executables/Services".

    Das vom Autor entwickelte Tool DRV_Load implemetiert mit Hilfe der Delphi Unit drivers.pas die beschriebenen Funktionen. Das Programm stellt eine grafische Oberfläche zum dynamischen Laden und Entladen von Treibern zur Verfügung und ist, gerade in der Entwicklungsphase eines Treibers, ein nützliches Hilfsmittel. Der Quellcode des Programms und der Unit, sowie eine kurze Programmbeschreibung befinden sich im Anhang.

     

  5. Installation eine Treibers mit INF Dateien
  6. Die Installation von Treibern mit Hilfe von INF Dateien ist ein weiterer Weg, Treiber in das System zu integrieren. In INF Dateien kann man festlegen, welche Dateien kopiert, gelöscht oder umbenannt werden sollen. Man kann Registry Änderungen vornehmen und man kann auf recht einfache Weise Treiber am SCM anmelden.

    INF Dateien können mit einem einfachen Texteditor erstellt und bearbeitet werden. Jede INF Datei besteht aus mehreren sogenannten Sektionen, die jeweils verschiedene Aufgaben haben, wie z.B. Dateien kopieren oder einen Wert in der Registry setzen. Es gibt etwa 20 verschiedene Typen von Sektionen. Eine genaue Beschreibung der Typen und deren Verwendung findet man in [PrgGd96].

    Im folgenden Beispiel wird die Verwendungen einiger Sektionen kurz erläutert:

    
    
    ;
    ; Installationsfile für den Beispieltreiber K
    ;
    
    ; die Versionssektion muss immer vorhanden sein
    [Version]
    
    Signature="$Windows NT$"
    ; INF File soll nur unter NT ausgefuehrt werden
    
    
    [DefaultInstall]
    ; Die Installationssektion DefaultInstall wird
    ; automatisch aufgerufen, wenn die INF Datei
    ; ueber den Explorer ausgefuehrt wird.
    
    CopyFiles=K_Files
    ; CopyFiles - Dateien sollen kopiert werden.
    ; Welche Dateien das sind, wird in der Sektion
    ; [K_Files] festgelegt.
    
    
    [DefaultInstall.Services]
    ; eine Untersektion von [DefaultInstall]
    ; Die Erweiterung ".Services" gibt an, dass
    ; ein Dienst/Treiber (de-)installiert werden
    ; soll
     
    AddService= K,,K_ServiceInst
    ; AddService - ein Dienst/Treiber wird
    ; installiert. Der Name des Dienstes ist
    ; hier "K". Die Parameter fuer den Dienst
    ; werden in der Sektion [K_ServiceInst]
    ; festgelegt.
    
    
    [K_Files]
    ; Eine CopyFile Sektion, auf die von der
    ; Sektion [DefaultInstall] verwiesen wird.
    ; Hier werden die zu kopierenden Files angegeben.
    
    k.sys
    
    
    [SourceDisksNames]
    ; Liste der verschiedenen Quellverzeichnisse
    
    1=%K_Name%,,
    ; In diesem Beispiel wird kein Pfad angegeben, das
    ; bedeutet, dass im aktuellen Verzeichnis gesucht
    ; wird.
    
    
    [SourceDisksFiles]
    ; Zuordnung der Files zu den Quellverzeichnissen
    
    k.sys=1
    ; Fuer diese Datei wird das Quellverzeichnis "1",
    ; das in der Sektion [SourceDisksNames] definiert
    ; wurde, verwendet.
    
    
    [DestinationDirs]
    ; In dieser Sektion erfolgt die
    ; Zuordnung der CopyFile Sektionen
    ; zu einem Zielverzeichnis
    
    K_Files=12
    ; Die Dateien der Sektion [K_Files] sollen in
    ; das Verzeichnis 12 ( %system32%\drivers -
    ; wird vom System definiert) kopiert werden.
    
    
    [K_ServiceInst]
    ; eine Service Install Sektion, auf die von
    ; der Sektion [DefaultInstall.Services]
    ; verwiesen wurde.
    ; Hier werden die Informationen fuer die
    ; Installtion des Dienstes/Treibers angegeben.
    DisplayName=%K_Name%
    ServiceType=1              ; SERVICE_KERNEL_DRIVER
    StartType=1                ; SERVICE_SYSTEM_START
    ErrorControl=1             ; SERVICE_ERROR_NORMAL
    ServiceBinary=%12%\k.sys
    
    
    [STRINGS]
    ; Liste der verwendeten Strings
    K_Name="K Beispieltreiber"
    
    
    ;
    ; Installationsfile für den Beispieltreiber K
    ;
    
    ; die Versionssektion muss immer vorhanden sein
    [Version]
    
    Signature="$Windows NT$"
    ; INF File soll nur unter NT ausgefuehrt werden
    
    
    [DefaultInstall]
    ; Die Installationssektion DefaultInstall wird
    ; automatisch aufgerufen, wenn die INF Datei
    ; ueber den Explorer ausgefuehrt wird.
    
    CopyFiles=K_Files
    ; CopyFiles - Dateien sollen kopiert werden.
    ; Welche Dateien das sind, wird in der Sektion
    ; [K_Files] festgelegt.
    
    
    [DefaultInstall.Services]
    ; eine Untersektion von [DefaultInstall]
    ; Die Erweiterung ".Services" gibt an, dass
    ; ein Dienst/Treiber (de-)installiert werden
    ; soll
     
    AddService= K,,K_ServiceInst
    ; AddService - ein Dienst/Treiber wird
    ; installiert. Der Name des Dienstes ist
    ; hier "K". Die Parameter fuer den Dienst
    ; werden in der Sektion [K_ServiceInst]
    ; festgelegt.
    
    
    [K_Files]
    ; Eine CopyFile Sektion, auf die von der
    ; Sektion [DefaultInstall] verwiesen wird.
    ; Hier werden die zu kopierenden Files angegeben.
    
    k.sys
    
    
    [SourceDisksNames]
    ; Liste der verschiedenen Quellverzeichnisse
    
    1=%K_Name%,,
    ; In diesem Beispiel wird kein Pfad angegeben, das
    ; bedeutet, dass im aktuellen Verzeichnis gesucht
    ; wird.
    
    
    [SourceDisksFiles]
    ; Zuordnung der Files zu den Quellverzeichnissen
    
    k.sys=1
    ; Fuer diese Datei wird das Quellverzeichnis "1",
    ; das in der Sektion [SourceDisksNames] definiert
    ; wurde, verwendet.
    
    
    [DestinationDirs]
    ; In dieser Sektion erfolgt die
    ; Zuordnung der CopyFile Sektionen
    ; zu einem Zielverzeichnis
    
    K_Files=12
    ; Die Dateien der Sektion [K_Files] sollen in
    ; das Verzeichnis 12 ( %system32%\drivers -
    ; wird vom System definiert) kopiert werden.
    
    
    [K_ServiceInst]
    ; eine Service Install Sektion, auf die von
    ; der Sektion [DefaultInstall.Services]
    ; verwiesen wurde.
    ; Hier werden die Informationen fuer die
    ; Installtion des Dienstes/Treibers angegeben.
    DisplayName=%K_Name%
    ServiceType=1              ; SERVICE_KERNEL_DRIVER
    StartType=1                ; SERVICE_SYSTEM_START
    ErrorControl=1             ; SERVICE_ERROR_NORMAL
    ServiceBinary=%12%\k.sys
    
    
    [STRINGS]
    ; Liste der verwendeten Strings
    K_Name="K Beispieltreiber"
    
    
    

     

4.2 Struktur eines Treibers zurück nach oben nächster Abschnitt Inhaltsverzeichnis

Ein Treiber wird nicht wie eine "normale" Anwendung mehr oder weniger sequentiell abgearbeitet. Vielmehr werden in bestimmten Situationen einzelne Funktionen direkt vom I/O Manager aufgerufen. Allgemeine Ereignisse, auf die ein Treiber reagiert, sind z.B.

Nachfolgend werden die wichtigsten Treiberfunktionen kurz beschrieben.

 

4.2.1 Treiberinitialisierungs- und Aufräumfunktionen zurück nach oben nächster Abschnitt Inhaltsverzeichnis

Bevor ein Treiber I/O Anfragen bearbeiten kann, sind normalerweise eine Reihe von Initialisierungen notwendig. Gleiches gilt, wenn der Treiber beendet wird. In diesem Fall sollten belegte Ressourcen freigegeben und die Hardware in einen stabilen Zustand versetzt werden.

    Treibereintrittspunkt (DriverEntry)

Wenn der Treiber gestartet wird, ruft der I/O Manager die Funktion auf. Dies kann zum Zeitpunkt des Systemstarts sein, aber auch später, wenn der Treiber manuell gestartet wird. Die Funktion hat u.a. folgende Aufgaben:

 

    Unload Routine

Diese Funktion wird vom I/O Manager aufgerufen, wenn ein Treiber manuell bzw. über die Systemsteuerung beendet wird. Beim Entladen des Treibers müssen die belegten Hard- und Software Ressourcen wieder freigegeben werden.

 

    Shutdown Routine

Beim Herunterfahren des Systems wird diese Routine aufgerufen. Da das System sowieso beendet wird, ist es in diesem Fall eher unwichtig, daß belegte Ressourcen freigegeben werden. Vielmehr sollte die Hardware in einen stabilen Zustand versetzt werden.

 

    Bugcheck Callback Routine

Soll ein Treiber im Falle eines Systemabsturzes aufgerufen werden, kann er diese Routine registrieren. Auch hier sollte die Hardware in einen stabilen Zustand versetzt werden. Zusätzlich können Statusinformationen ausgegeben werden, die bei der Analyse des Absturzes hilfreich sein können.

 

4.2.2 Dispatch Routinen zurück nach oben nächster Abschnitt Inhaltsverzeichnis

Bei einer I/O Anfrage ruft der I/O Manager parameterabhängig eine der Dispatch Routinen des Treibers auf. Prinzipiell wird jede (zulässige) I/O Anfrage von einer Dispatch Routine verarbeitet.

 

    Open/Close Operationen

Alle Treiber, die mit Usermode Programmen kommunizieren wollen, müssen eine Dispatch Routine haben, die den Win32-Aufruf CreateFile verarbeitet. Wenn Aufräumarbeiten notwendig sein sollten, kann eine Routine deklariert werden, die den CloseHandle Aufruf behandelt.

 

    Geräte Operationen

Abhängig vom Gerätetyp können vom Treiber verschiedene Routinen zur Behandlung und Verarbeitung bzw. zur Steuerung des Gerätes angeboten werden. Diese Routinen werden bei den Win32 Funktionen ReadFile, WriteFile und DeviceIOControl vom I/O Manager aufgerufen.

 

4.2.3 Routinen für denDatentransfer zurück nach oben nächster Abschnitt Inhaltsverzeichnis

 

    Start I/O Routine

Um einen Datentransfer von bzw. zu einem Gerät zu initiieren, wird vom I/O Manager die Start I/O Routine aufgerufen.

 

    Interrupt Service Routinen (ISR)

Wird von einem Gerät ein Interrupt generiert, ruft der Interrupt Dispatcher die entsprechende Service Routine auf. Hier wird der Interrupt bestätigt und alle notwendigen Informationen für den späteren Gebrauch werden gespeichert. Danach wird mit Hilfe des I/O Managers eine DPC Routine in die Warteschlange eingefügt.

 

    DPC Routinen

Nach Beendigung der ISR werden alle weiteren Arbeiten, wie z.B. Freigabe von Ressourcen, Fehlermeldungen oder Rückgabe von Ergebnissen an den I/O Manager, in der DPC Routine durchgeführt.

Die Anzahl der DPC Routinen ist vom Treiber abhängig. Für Treiber, die mit einer DPC Routine auskommen, bietet der I/O Manager einen vereinfachten Mechanismus, genannt DpcForIsr.

 

4.2.4 Synchronisations-/ Rückruffunktionen zurück nach oben nächster Abschnitt Inhaltsverzeichnis

Windows NT ist ein Multitaskingsystem. Ein Treiber muß deshalb mehrere I/O Anfragen gleichzeitig verarbeiten und verwalten können. So kann beispielsweise ein User-Programm eine Datei von einer Diskette lesen, während ein anderes Programm auf die gleiche Diskette schreiben will.

Der I/O Manager bietet einige Funktionen, um solche Situationen zu behandeln.

 

    ControllerControl Routine

Controller unterstützen in der Regel mehrere physikalische Geräte. Deshalb ist es wichtig, daß immer nur eine Operation zur gleichen Zeit auf dem Gerät ausgeführt wird. Bevor also der Zugriff auf die Register des Controllers erfolgt, wird von der Start I/O Routine das exklusive Zugriffsrecht beantragt. Wenn der Zugriff gewährt wird, wird die ControllerControl Rückruffunktion aufgerufen, ansonsten wird solange gewartet, bis der Controller zur Verfügung steht.

 

    AdapterControl Routine

Ein spezieller Controller ist der DMA-Controller. Auch er kann von mehreren Geräten beansprucht werden. Wenn ein Treiber den DMA benutzen möchte, beantragt er das exklusive Zugriffsrecht. Wird der Zugriff gewährt, kommt die AdapterControl Rückruffunktion zur Ausführung, andernfalls wird solange gewartet, bis der DMA-Controller freigegeben wird.

 

    SynchCritSection Routinen

Es ist möglich, daß eine ISR aufgerufen wird, während sich eine DPC Funktion in der Ausführung befindet. Damit es nicht zu Konflikten kommt, wenn beide auf die gleichen Ressourcen zugreifen, werden die kritischen Teile der DPC Funktion in einer SynchCritSection Routine durchgeführt.

Innerhalb von SynchCritSection Routinen wird der IRQL auf den der ISR angehoben, so daß diese nicht zur Ausführung kommt. Bei Multiprozessor Rechnern funktioniert dieser Mechanismus nicht. Dort werden sogenannte Spinlocks benutzt, die einem Prozessor exklusive Zugriffsrechte auf Datenstrukturen gewähren.

 

4.2.5 Andere Treiberfunktionen zurück nach oben nächster Abschnitt Inhaltsverzeichnis

 

    Timer Funktionen

Für die zeitliche Kontrolle stehen dem Treiber I/O Timer oder CustumTimerDPC Routinen zur Verfügung.

 

    I/O Completion Routinen

Wenn Treiber in höheren Schichten Funktionen von tieferliegenden Treibern aufrufen, ist es oftmals notwendig, daß sie nach Beendigung der Funktion informiert werden. Dafür werden I/O Completion Routinen verwendet.

 

    Cancel I/O Routinen

Für Anfragen, die unter Umständen sehr lange dauern können, ist es sinnvoll, eine Funktion zu deklarieren, die die Anfrage abbricht. In dieser sogenannten Cancel I/O Funktion müssen alle notwendigen Aufräumarbeiten durchgeführt werden.

 

4.3 Zugriffsmechanismen auf Speicherpuffer zurück nach oben nächster Abschnitt Inhaltsverzeichnis

Wenn ein Usermode Programm eine I/O Anfrage startet, ergibt sich das Problem, daß eventuell übergebene Puffer im ausgelagerten Bereich des virtuellen Speichers liegen können. Code, der mit einem IRQL größer oder gleich DISPATCH_LEVEL ausgeführt wird, darf aber nicht auf solche Speicherbereiche zugreifen. Ein anderes Problem ist, daß die physikalische Adresse des Puffers sich durchaus während einer Anfrage ändern kann, z.B. wenn Speicherseiten ausgelagert werden, während der Treiber die Anfrage bearbeitet.

Der I/O Manager bietet deshalb zwei verschiedene Möglichkeiten, um auf Puffer zuzugreifen. Welche Methode verwendet wird, legt man in der DriverEntry Routine fest.

 

    1. Bufferd I/O
    2. Bei dieser Methode belegt der I/O Manager am Anfang jeder I/O Operation einen Puffer im nicht ausgelagertem Bereich des Speichers und übergibt die Adresse des Puffers dem Treiber. Sollen Daten auf ein Gerät geschrieben werden, kopiert der I/O Manager Daten aus dem Benutzerpuffer in den Systempuffer. Wenn Daten gelesen werden sollen, kopiert der I/O Manager, nachdem der Treiber die Operation abgeschlossen hat, Daten aus dem Systempuffer in den Benutzerpuffer.

       

    3. Direct I/O
    4. Hier wird das Kopieren von Daten vermieden, indem der Treiber direkten Zugriff auf den Benutzerpuffer bekommt. Am Anfang jeder I/O Operation wird, um Pagefaults zu vermeiden, der gesamte Benutzerpuffer im Speicher gelockt. Danach bildet der I/O Manager eine Liste der physikalischen Speicherseiten, die der Benutzerpuffer belegt. Diese Liste wird dem Treiber übergeben. Nachdem die Operation abgeschlossen ist, werden die gelockten Seiten vom I/O Manager wieder freigegeben.





written 1998/1999 by Guido Wischrop
all rights reserved