Betriebssysteme · Institut für Systemarchitektur · Fakultät Informatik · TU Dresden |
|
2. Übung - Der erste "richtige" TreiberInhalt:
1. Schreiben einer Treiber-Start Routine:Wie Sie aus der Vorlesung wissen, wird für jeden Treiber eine Datenstruktur im Kern verwaltet. Diese Datenstruktur wird angelegt, wenn der Treiber vom Kern geladen wird und muss von einer Treiber-Start Routine initialisiert werden. So müssen u.a. die Funktionen für verschiedene Aufrufe des Treibers - wie Lesen und Schreiben - registriert werden und es muss festgelegt werden aus welchem Memory-Pool der Treiber seinen Speicher bezieht. Damit der Kern weiss, welche Funktion er am Anfang aufrufen muss, hat diese einen speziellen Namen. Unter Windows heisst die Treiber-Start Routine "DriverEntry". Damit sie auch die richtigen Parameter in der richtigen Reihenfolge übergeben bekommt, muss sie einige Konventionen einhalten. Das Skelett dieser Funktion sieht wie folgt aus:
NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING
pRegistryPath)
Arbeiten, welche von der DriverEntry Routine durchgeführt werden, umfassen:
2. Registrieren eines Gerätes:Innerhalb der DriverEntry Funktion muss jetzt ein Gerät angelegt werden und ein symbolischer Name auf dieses Gerät erzeugt werden. Nutzen Sie zum Erzeugen des Gerätes die Funktion IoCreateDevice (finden Sie mit Hilfe der Hilfe ;) heraus, welche Parameter die Funktion benötigt und welche Werte Sie dort übergeben müssen.). Himweis: Geben Sie als Device-Typ FILE_DEVICE_UNKNOWN, bei Device-Characteristics 0 und bei Device-Exclusive TRUE an.
Prüfen Sie den Erfolg des Aufrufen anhand des Rückgabe-Wertes.
Der Geräte-Name muss im Namensraum der Geräte angesiedelt sein. Dieser
beginnt immer mit '\Device' Danach folgt ein Name, welcher das Gerät
beschreibt gefolgt von einem Index (beginnend bei 0). (z.B.
'\Device\Harddisk0'). Um das Testen der Treiber zu vereinfachen wählen Sie
als Gerätenamen 'ABSDevice0'. Der volle Name lautet also
'\Device\ABSDevice0'.
Zum Registrieren des Gerätes im Namensraum für Win32 nutzen Sie die Funktion
IoCreateSymbolicLink. (Benutzen Sie wieder die Hilfe um Information über
Parameter herauszufinden.) Wenn DriverEntry einen Fehlercode zurückliefert, entfernt der I/O Manager den Treiber wieder vom System. Geschieht dies, sollten natürlich auch die regestrierten Geräte und Namen entfernt werden. Dies wird von einer Driver-Unload Routine durchgeführt, welche im Driver-Objekt im Feld DriverUnload registriert werden muss. Schreiben Sie eine solche Funktion und registrieren Sie diese. Die Driver-Unload Routine soll die Funktionen IoDeleteSymbolicLink und IoDeleteDevice benutzen. Damit die Driver-Unload Routine zu dem Geräte-Objekt und dem symbolischen Namen kommt, kann die Device-Extension Struktur genutzt werden. Eine Device-Extension Struktur enthält "global" Informationen über ein Gerät, welche der Treiber-Schreiber zusätzlich zu den schon vorhandenen Informationen nutzen will. Die Struktur wird beim Erzeugen eines Gerätes mit anglegt - die Grösse der Extension wird als zweiter Parameter übergeben. Auf sie kann mittels des DriverExtension Feldes des Driver-Objektes zugegriffen werden. Nutzen für Ihren Treiber folgende Struktur: typedef struct _DEVICE_EXTENSION { PDEVICE_OBJECT pDevice; UNICODE_STRING sDeviceName; // internal name UNICODE_STRING sSymLinkName; // external name } DEVICE_EXTENSION;
Die Werte der Struktur müssen beim Laden des Treibers gefüllt werden, damit
die Driver-Unload Routine sie herausfinden kann. extern "C" { #include <ntddk.h> }Gleiches gilt für eine Fehlermeldung "error LNK2001: unresolved external symbol _DriverEntry@8" . Um diesen Fehler zu beheben schreiben Sie vor NTSTATUS DriverEntry(...) 'extern "C"'. extern "C" NTSTATUS DriverEntry(...) zu 2. Testen / Installieren:
Um den Treiber zu testen, kopieren Sie die erhaltene .sys Datei in das
Verzeichnis C:\Winnt\System32\drivers und benennen Sie sie abstreiber.sys
(oder achten Sie auf den Dateinamen in den folgenden Schritten). Rufen Sie
regedit auf und suchen Sie den Pfad
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services. Erzeugen Sie darin ein
Unterverzeichnis (Schlüssel) 'abstreiber' (Achtung Dateiname! ohne .sys).
Innerhalb dieses Unterverzeichnisses legen Sie drei DWORD Werte an: ErrorControl=1 Start=3 Type=1Sie können auch noch einen String Wert anlegen, welcher den in der Management Console angezeigten Namen enthält (DisplayName="ABSTreiber"). Haben Sie das Unterverzeichnis neu hinzugefügt, müssen Sie den Computer neu booten. Gab es den Eintrag schon, reicht ein Austauschen der .sys Datei. Da der Start-Wert auf 3 gesetzt wurde, wird der Treiber nur bei Bedarf geladen. Wir können das manuell veranlassen indem wir in einer Konsole "net start abstreiber" eingeben. Die Funktion des Treiber können wir jetzt mit dem WinObj Tool von Sysinternals kontrollieren: Das Objekt '\Device\ABSDriver0' und der Symbolic Link '\??\ABS1' sollten jetzt zu sehen sein. Der Teiber kann mit dem Befehl "net stop abstreiber" wieder entfernt werden. 3. Ein kleiner Loopback-TreiberUm das angelegte Gerät nutzen zu können, muss es möglich sein, ein Handle auf dieses Gerät zu bekommen und wieder frei zu geben. Dies wird über die Win32-Funktionen CreateFile und CloseHandle realisiert. Zudem sollte es möglich sein, Daten zum Gerät zu senden und vom Gerät zu beziehen (ReadFile und WriteFile). Diese Funktionen der Win32 API werden auf sogenannte Dispatch Routinen des Treibers abgebildet, welche dieser auch implementieren muss. Diese Funktionen werden in der DriverEntry Routine im Device-Objekt registriert. Die Create Methode z.B. so: pDriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;Der Unterschied zur Driver-Unload Routine ist, dass hier ein Array von Funktionen verwendet wird. Diese Dispatch Routinen sehen alle gleich aus. z.B. die Create Methode: static NTSTATUS DispatchCreate (IN PDEVICE_OBJECT pDevObj,IN PIRP pIrp);Wie man sieht, bekommt jede Funktion einen Zeiger auf das Geräte-Objekt und das aktuelle IRP. Um eine Anfrage abzuhandeln, müssen im IRP nach Abhandlung einige Werte gesetzt und der IRP abgeschlossen werden. Welche das sind, sollen Sie herausfinden (siehe Hilfe zu IRP-Struktur). Zum Abschliessen eines IRP benutzen Sie die Funktion IoCompleteRequest. Die Device-Extension, wie folgt erweitert wird sie: typedef struct _DEVICE_EXTENSION { PDEVICE_OBJECT pDevice; UNICODE_STRING sDeviceName; // internal name UNICODE_STRING sSymLinkName; // external name // Reserve space for pointer to loopback buffer PVOID pDeviceBuffer; ULONG nDeviceBufferSize; } DEVICE_EXTENSION;
Die einzelnen Dispatch-Routinen sollen folgende Funktionen implementieren:
Um diese Funktionen realisieren zu können, nutzen Sie zum Puffer-Management die Funktionen: ExAllocatePool, ExFreePool und RtlCopyMemory. Speicher für den internen Puffer soll vom PagedPool kommen. Wie Sie an die Grösse einer Anfrage (wieviele Bytes übertragen werden) herankommen, entnehmen Sie der Hilfe (Hinweis: IO_STACK_LOCATION und IRP ist ein guter Anfang). 4. Anpassen der TestapplikationPassen Sie die Testapplikation der letzten Übung so an, dass Sie das Gerät ABS1 öffnet, Daten auf das Gerät schreibt und von ihm liest. Anmerkungen:
Ich bin gebeten wurden Euch auf die Dokumentation des DDK hinzuweisen
(wo dann die Sachen wie
|
webmaster@os, home |
25. Jun 2004
|
· Copyright © 2001-2022 Operating Systems Group, TU Dresden | Impressum · |