4.4 Datenstrukturen
4.4.1 I/O Request Packets (IRPs)
Wenn ein Usermode Programm eine I/O Anfrage startet, wird zunächst der I/O Manager aufgerufen. Dieser erzeugt nun eine Datenstruktur in einem nicht ausgelagerten Speicherbereich des System, die alle notwendigen Daten zur Abarbeitung der Anfrage enthält. Das IRP genannte Objekt wird danach an eine entsprechende Dispatch-Routine eines Treibers weitergeleitet. Die Dispatch Routine überprüft die an sie übergebenen Parameter und ruft entweder die Start I/O Routine oder eine weitere Dispatch-Routine eines tieferliegenden Treibers auf. Nach der vollständigen Bearbeitung der Anfrage werden Statusinformationen im IRP gespeichert und es wird wieder an den I/O Manager übergeben. Der übergibt die Daten des IRP an das Usermode Programm und schließt die Anfrage ab.
Ein IRP besteht aus einem festen Teil, dem IRP Header, und einem oder mehreren Parameterblöcken, sogenannten I/O Stack Locations.
Der Header enthält u.a. Daten über die Art und Größe der Anfrage, Statusinformationen und einen Zeiger auf einen Puffer, der zum Datenaustausch zwischen Treiber und Benutzeranwendung verwendet wird.
Der Parameterblock enthält den Funktionscode der Anfrage und funktionsspezifische Parameter.
Abbildung 4.4.1-1: IRP Aufbau
Es sind nicht alle Teile der IRP Struktur dokumentiert, allerdings kann man in der Datei ntddk.h die vollständige Deklaration finden. In [Kernel96] ist der Aufbau wie folgt beschrieben:
typedef struct _IRP { . . PMDL MdlAddress; ULONG Flags; union { struct _IRP *MasterIrp; . . PVOID SystemBuffer; } AssociatedIrp; . . IO_STATUS_BLOCK IoStatus; KPROCESSOR_MODE RequestorMode; . . BOOLEAN Cancel; KIRQL CancelIrql; . . PDRIVER_CANCEL CancelRoutine; PVOID UserBuffer; union { struct { . . union { KDEVICE_QUEUE_ENTRY DeviceQueueEntry; struct { PVOID DriverContext[4]; }; }; . . PETHREAD Thread; . . LIST_ENTRY ListEntry; . . } Overlay; . . } Tail; } IRP, *PIRP;
zeigt auf eine Speicherbeschreibungsliste (Memory Descriptor List – MDL) für den Benutzerpuffer, wenn der Treiber im Direct I/O Modus arbeitet und eine IRP_MJ_READ oder IRP_MJ_WRITE Aktion durchgeführt werden soll.
Dieses Feld darf nur gelesen werden und kann folgende Zustände annehmen:
IRP_NOCACHE
IRP_PAGING_IO
IRP_MOUNT_COMPLETION
IRP_SYNCHRONOUS_API
IRP_ASSOCIATED_IRP
IRP_BUFFERED_IO
IRP_DEALLOCATE_BUFFER
IRP_INPUT_OPERATION
IRP_SYNCHRONOUS_PAGING_IO
IRP_CREATE_OPERATION
IRP_READ_OPERATION
IRP_WRITE_OPERATION
IRP_CLOSE_OPERATION
IRP_DEFER_IO_COMPLETION
zeigt auf das Master IRP, das von einem Treiber in der höchsten Ebene durch einen Aufruf von IoMakeAssociatedIrp erzeugt wurde.
zeigt auf einen Systemspeicherbereich für eine der folgenden Aktionen:
In jedem der Fälle werden Daten von bzw. auf diesen Speicherbereich übertragen.
ist der I/O Statusblock, in den vor Aufruf der Funktion IoCompleteRequest Statusinformationen gespeichert werden.
gibt den Modus an, aus dem die Anfrage gestartet wurde, und hat entweder den Wert UserMode oder KernelMode
Hat diese Variable den Wert TRUE, wurde oder wird die Anfrage abgebrochen.
gibt den IRQL des Treibers an, wenn IoAcquireCancelSpinLock aufgerufen wird.
gibt die Adresse einer Cancel Routine an, die aufgerufen werden soll, wenn die Anfrage abgebrochen werden soll. Ist der Wert NULL, kann die Anfrage nicht abgebrochen werden.
gibt die Adresse eines Ausgabepuffers an, wenn der Major Funktionscode den Wert IRP_MJ_INTERNAL_DEVICE_CONTROL und der I/O Control Code den Wert METHOD_NEITHER hat.
Wenn IRPs in der Gerätewarteschlange stehen, verbindet dieser Wert die IRPs in der Warteschlange. Der Wert ist nur gültig, wenn das IRP gerade vom Treiber verarbeitet wird.
Wenn keine IRPs in der Warteschlange stehen, kann der Treiber hier bis zu vier Zeiger ablegen. Auf das Feld kann nur zugegriffen werden, wenn das IRP dem Treiber selbst gehört.
zeigt auf den Thread Control Block des Aufrufers.
Wenn der Treiber eine eigene Warteschlange für IRPs verwaltet, wird dieser Wert benutzt um die IRPs miteinander zu verbinden.
Jedes Treiberobjekt repräsentiert ein Abbild eines geladenen Kernelmode Treibers und enthält alle Einsprungadressen des Treibers. Wenn ein Treiber geladen wird, erzeugt der I/O Manager ein neues Treiberobjekt und ruft die Initialisierungsroutine DriverEntry auf. Dort werden alle weiteren Einsprungadressen in das Objekt eingetragen. Sollte der Treiber sich nicht initialisieren können, wird das Treiberobjekt wieder gelöscht. Bei einer I/O Anfrage benutzt der I/O Manager das Objekt, um die richtige Dispatch Routine zu finden und aufzurufen. Die Abbildung 4.4.2-1 zeigt die Struktur eines Treiberobjekts.
Abbildung 4.4.2-1: Das Treiberobjekt
Das Treiberobjekt ist nicht vollständig dokumentiert, die Deklaration findet man aber in der Datei ntddk.h. In [Kernel96] sind folgende Teile beschrieben:
zeigt auf ein oder mehrere Device Objects, die vom Treiber erstellt wurden. Dieser Wert wird automatisch aktualisiert, wenn die Funktion IoCreateDevice erfolgreich aufgerufen wurde. In der Unload Routine werden dieser Wert und der Wert NextDevice von dem Device Object benutzt, um die Funktion IoDeleteDevice für alle vom Treiber erzeugten Device Objects aufzurufen.
zeigt auf die Hardware Konfigurationsinformationen in der Registry.
zeigt auf eine Struktur, die die Einsprungadressen des Treibers für Fast I/O enthält. Dieser Wert wird nur von FSDs und Netzwerk Transport Treibern benutzt.
zeigt auf den Eintrittspunkt der DriverEntry Routine und wird vom I/O Manager initialisiert. Die DriverEntry Routine ist wie folgt deklariert:
NTSTATUS
zeigt auf die Einsprungadresse der StartIo Routine und wird von der DriverEntry Routine initialisiert. Hat der Treiber keine StartIo Routine, ist der Wert NULL. Die StartIo Routine ist wie folgt deklariert:
VOID
Läßt sich der Treiber entladen, wird der Eintrittspunkt der Unload Routine während der Initialisierung des Treibers von der DriverEntry Routine hier abgelegt, ansonsten ist der Wert NULL. Die Unload Routine ist wie folgt deklariert:
VOID
ist ein Feld mit einem und mehreren Eintrittspunkten von Dispatch Routinen des Treibers. Hier muß jeder Treiber mindestens einen Eintrittspunkt für IRP_MJ_XXX Funktionen, die er behandelt, festlegen. Es können so viele Eintrittspunkte festgelegt werden, wie IRP_MY_XXX Codes behandelt werden.
Eine Dispatch Routine ist wie folgt deklariert:
NTSTATUS
4.4.3 Das Geräteobjekt und Geräteerweiterungen (DeviceObject / DeviceExtension)
Für jedes virtuelle, logische und physische Gerät im System gibt es ein Geräteobjekt, das Informationen über die Eigenschaften und den Status des Gerätes enthält. Die Abbildung 4.4.3-1 zeigt die allgemeine Struktur eines Geräteobjektes und die Beziehung zu anderen Objekten.
Abbildung 4.4.3-1: Das Geräteobjekt
Die DriverEntry Routine erzeugt für jedes Gerät, das der Treiber kontrolliert, ein Geräteobjekt mit Hilfe der Funktion IoCreateDevice. Über die Variable NextDevice kann der Treiber dann nacheinander die verschiedenen Geräte abfragen. Die Treiberfunktionen können einen Teil des Geräteobjektes, die Geräteerweiterung (DeviceExtension), zum Speichern von Daten und Statusinformationen verwenden. Dabei wird die Struktur der Erweiterung vom Treiber selbst festgelegt.
Die Unload Routine des Treibers ist für das Entfernen der/des Objekt/es zuständig. Wenn mehrere Geräteobjekte erstellt wurden, muß der Treiber über NextDevice alle Objekte abfragen und löschen.
Auch das Geräteobjekt ist nicht vollständig dokumentiert. Die komplette Deklaration ist wiederum in der Datei ntddk.h zu finden. In [Kernel96] werden folgende Strukturen beschrieben:
zeigt auf das zugehörige Treiberobjekt des Gerätes.
zeigt auf das nächste Geräteobjekt, wenn der Treiber mehrere Geräte verwaltet. Der I/O Manager aktualisiert die Liste nach jedem erfolgreichen Aufruf der Funktion IoCreateDevice.
zeigt auf das gegenwärtige IRP, wenn der Treiber gerade ein IRP verarbeitet. Ansonsten ist der Wert NULL.
Nachdem das Geräteobjekt erzeugt wurde, setzt der Treiber diesen Wert auf "Flags OR BufferAccess" wobei BufferAccess entweder DO_BUFFERED_IO oder DO_DIRECT_IO ist. HigherLevel-Treiber führen "Flags OR LowerLevelDriverFlags" aus.
wird auf einen der folgenden Werte gesetzt, wenn ein Treiber für Wechseldatenträger die Funktion IoCreateDevice mit einem der entsprechenden Werte aufruft:
zeigt auf die Geräteerweiterung. Die Struktur und der Inhalt der Geräteerweiterung wird vom Treiber festgelegt. Die Größe der Struktur muß der Funktion IoCreateDevice als Parameter übergeben werden.
wird beim Aufruf der Funktion IoCreateDevice auf den Wert des Parameters DeviceType der Funktion gesetzt und legt den Typ des Gerätes fest. Wenn keine der Systemkonstanten FILE_DEVICE_XXX zutreffend sind, können eigene Werte im Bereich von 32768 bis 65535 definiert werden. Eine Liste der FILE_DEVICE_XXX Werte findet man in [Kernel96] Reference Part 2. Der Bereich von 0 bis 32767 ist von Microsoft reserviert.
gibt die minimale Anzahl der StackLocations in einem IRP an, das dem Treiber übergeben wird. Die Funktion IoCreateDevice setzt diesen Wert auf 1. Der I/O Manager aktualisiert den Wert automatisch, wenn übergeordnete Treiber die Funktion IoAttachDevice oder IoAttachDeviceToDeviceStack aufrufen. Höher liegende Treiber, die sich selbst mit IoGetDevicePointer über andere Treiber legen, müssen StackSize in ihrem eigenen Treiberobjekt explizit auf (1 + StackSize des tieferliegenden Treibers) setzen.
Jeder Treiber setzt diesen Wert auf das benötigte Alignment des Treibers – 1 oder auf einen der Systemwerte:
Höher liegende Treiber benutzen den Wert des zugrundeliegenden Treibers.
4.5.1 Debugging mit WinDbg
WinDbg ist ein Debugger mit grafischer Oberfläche, der das Debuggen von Kernelmode Treibern auf Quellcodeebene ermöglicht. Wie im vorherigen Kapitel beschrieben, müssen dazu zwei Rechner vorhanden sein, die mit einem seriellen Kabel verbunden sind.
Hinweis:
Der beim MSDN mitgelieferte WinDbg Version 4.00 ist offensichtlich veraltet und außerdem sehr instabil. Deshalb empfiehlt der Autor die Version WinDbg 5.00.1719.1, die separat im Internet von Microsofts Webseiten bezogen werden kann.Um das Debugging zur aktivieren, muß am Zielrechner die Option /DEBUG in der Datei boot.ini eingetragen werden (Fußnote 10) und der Treiber muß in der Checked Build Umgebung übersetzt werden. Anschließend muß entweder der übersetzte Treiber oder das extrahierte Symbolfile in das Symbol-Verzeichnis des Debuggers kopiert werden. Zum Auslesen der Symbolinformationen aus dem Treiber verwendet man die beiden Tools dumpbin und rebase. Das Batch MAKEDBG.BAT demonstriert den Aufruf der Tools:
@echo off e: if %"1"=="" goto fehler cd %1\i386\checked echo %1 dir dumpbin /headers %2.sys | findstr /i /c:"image base" echo 10000 image base - OK ??? Ctrl+C fuer Abbruch pause rebase -b 0x10000 -x sys %2.sys copy sys\sys\%2.dbg e:\checked\symbols\sys goto ende :fehler echo Aufruf: echo. echo makedbg Verzeichnis Treiber echo. :ende
Im Debugger selbst muß unter Optionen das Kernedebugging aktiviert werden. Die Abbildung 4.5.1-1 zeigt das Dialogfenster. Die dort eingestellten Werte sind Empfehlungen des Autors.
Abbildung 4.5.1-1: Aktivierung des Kerneldebuggings in WinDbg
Für das Debugging im Kernelmodus stehen zwei Funktionen zur Verfügung:
Da der Compiler in der Checked Build Umgebung das Symbol DBG definiert, kann man außerdem mit Hilfe der bedingten Compilierung zusätzlichen Code zur Fehlersuche erzeugen. Der Autor verwendet diese Funktionalität in allen Treibern in einem Macro, das die Debugmeldungen nur in der Checked Build Umgebung in den Treiber einbindet:
#if DBG
#define DbgPrint(arg) DbgPrint arg
#else
#define DbgPrint(arg)
#endif
Die Tabelle 4.5.1-1 zeigt eine kleine Auswahl von WindDbg Kommandos. Eine vollständige Beschreibung alle Kommandos findet man in der Online Hilfe des Debuggers.
Kommando |
Funktion |
help |
zeigt eine kurze Hilfe für die grundlegenden Kommandos an |
!help |
zeigt eine kurze Hilfe für die Standarderweiterungen von WinDbg an |
k, kb, kn, ks, kv |
Ausgabe eines Ausschnitts des gegenwärtigen Kernelmode Stacks |
!process 0 0 |
listet alle Prozesse des Systems |
!drivers |
listet alle geladenen Kernelmode Treiber |
!pcr |
zeigt die Kontextinformationen der 80x86 CPU |
!irpzone |
listet die zur Zeit benutzten IRPs |
!irp adresse |
zeigt den Inhalt eines IRPs |
!devobj adresse |
zeigt den Inhalt eines Geräteobjektes |
!drvobj adresse |
zeigt den Inhalt eines Treiberobjektes |
.kill PID |
beendet den angegebenen Prozess |
BE, BD, BL |
Breakpoints aktivieren (BE) Breakpoints deaktivieren (BD) Breakpoints auflisten (BL) |
Tabelle 4.5.1-1: WinDbg Kommandos
4.5.2 Der Blue Screen of Death (BSOD)
Wer sich längere Zeit mit der Treiberentwicklung für Windows NT beschäftigt, wird mit ziemlicher Sicherheit früher oder später mit einem blauen Bildschirm konfrontiert, der allgemein als Blue Screen of Death bekannt ist. Dieser Bildschirm wird angezeigt, wenn durch einen schweren Fehler die Arbeit des Betriebssystems nicht fortgesetzt werden kann bzw. dessen Stabilität stark beeinträchtigt wäre. Die Ursache für den Absturz kann eine unbehandelte Exception (Fußnote 12) sein, die im Kernelmodus aufgetreten ist. Es kann aber auch ein Treiber im Falle eines Fehlers mit Hilfe der Funktion KeBugCheck die Beendigung des Betriebssystems veranlassen. Der BSOD enthält einige nützliche Informationen, die Auskunft über die Ursache des Absturzes geben können. Diese Informationen sind in mehrere Abschnitte gegliedert, die nachfolgend kurz erläutert werden. Die Abbildung 4.5.2-1 erklärt die Aufteilung der Abschnitte.
Abbildung 4.5.2-1: Der Blue Screen of Death
Dieser Abschnitt gibt Auskunft über die Art des Fehlers. In der ersten Zeile werden ein Bugcheck Code und bis zu vier zusätzliche Bugcheck Parameter angezeigt.
Die zweite Zeile enthält den symbolischen Namen, der mit dem Bugcheck Code verknüpft ist, sofern der Code von Microsoft definiert wurde (Fußnote 13). Eine Erläuterung zu den Bugcheck Codes findet man in [WorkGuide97] und in [Baker97]. Sollte einer der Bugcheck Parameter eine Adresse angeben, werden in der zweiten Zeile auch die Basisadresse und der Name des Moduls angezeigt, das diese Speicheradresse belegt.
In der dritten Zeile werden der Prozessortyp , der IRQL zur Zeit des Absturzes und die Build Nummer von Windows NT angegeben. Da die Funktion KeBugCheck den IRQL auf HIGH_LEVEL erhöht, ist auf Intel Rechnern der IRQL immer 0x1F (auf Alpha Rechnern 0x07). Das höchstwertige Byte der Build Nummer gibt an, ob es sich um ein Free (0xF0) oder um ein Checked (0xC0) Build handelt.
Hier werden einige Informationen über die geladenen Treiber angezeigt. Die erste Spalte gibt die Basisadresse an, die zweite einen Zeitstempel und die dritte Spalte den Namen des Treibers. Der Zeitstempel enthält das Erstellungsdatum des Treibers in Sekunden seit 1970.
Der dritte Abschnitt zeigt einen Teil des Stacks. Jede Zeile repräsentiert ein Stack Frame, wobei das zuletzt aktive an erster Stelle steht. Die erste Spalte gibt die Adresse des Stack Frames an. Die nächsten beiden Spalten enthalten die Rücksprungadresse (Fußnote 14) und die vier Spalten danach geben die ersten vier DWORD Parameter an, die beim Aufruf der Funktion übergeben wurden. In der letzten Spalte wird der Name des Moduls angezeigt, auf das die Rücksprungadresse in Spalte 2 und 3 zeigt.
Im letzten Abschnitt wird angezeigt, ob eine Datei mit dem Speicherabbild (memory dump) erstellt wurde, und daß man beim wiederholtem Auftreten des Absturzes doch bitte seinen Administrator oder den technischen Support kontaktieren sollte. Ist der Kerneldebugger aktiv, werden außerdem Informationen über den Debugger Status ausgegeben.
Windows NT bietet mit dem Eventlogging einen standardisierten Mechanismus, um wichtige Ereignisse in einem systemweiten Logfile aufzuzeichnen. Gerade für Treiber, die normalerweise keine Möglichkeit haben, auf direkten Weg mit dem Anwender zu kommunizieren, ist dies oft der einzige Weg, um Informationen über bestimmte Vorgänge aufzuzeichnen.
Um möglichst unabhängig von der nationalen Sprache zu sein, werden beim Eventlogging nur Codes gespeichert. Bei Anzeige des Logfiles lädt der Viewer, je nach der aktuell ausgewählten Sprache, die passenden Nachrichten aus einem sogenannten Messagefile.
In der Abbildung 4.6-1 wird der Informationsfluß bei der Speicherung und beim Anzeigen der Daten vereinfacht dargestellt.
Abbildung 4.6-1: Eventlogging
Im folgenden werden die Grundlagen des Eventlogging erläutert. Dabei wird auf Beispiele verzichtet. Der Treiber k, der im nächsten Kapitel vorgestellt wird, demonstriert die hier vorgestellten Techniken ausführlich.
4.6.1 Aufbau der Message Codes
Ein Message Code ist ein 32 Bit Wert, der in mehrere Felder eingeteilt ist. Die Abbildung 4.6.1-1 erläutert den Aufbau und die Bedeutung der Felder. In der Datei ntiologc.h des Windows NT DDK sind eine Reihe von Standardcodes definiert. Die dazugehörigen Meldungen sind in der DLL iologmsg.dll abgelegt.
Abbildung 4.6.1-1: Aufbau der Message Codes
Um eigene Meldungen zu verwenden, muß man zunächst ein Definitionsfile erstellen und dies mit dem Messagecompiler (mc) übersetzen. Danach müssen die erzeugten Files in den Treiber eingebunden und der Treiber in der Registry als Eventlog-Komponente registriert werden.
4.6.2 Erstellen eines Definitionsfiles für den Messagecompiler
Das Definitionsfile hat die Dateiendung .mc und ist in zwei Abschnitte gegliedert. Der erste Abschnitt, auch Header Section genannt, enthält die Definitionen für Werte, die im zweiten Abschnitt, der Message Section, verwendet werden. Die folgenden Schlüsselworte können in der Header Section verwendet werden:
In der Message Section stehen folgende Schlüsselworte zur Verfügung:
Beim Übersetzen des Definitionsfiles mit dem Message Compiler (Fußnote 15) werden folgende Dateien erzeugt:
4.6.3 Einbinden in den Treiber
Die erzeugten Dateien müssen noch weiterverarbeitet werden. Dafür gibt es zwei Möglichkeiten. Man kann sie in eine externe DLL einbinden oder man integriert sie direkt in den Treiber. Hier soll nur die zweite Möglichkeit kurz erläutert werden.
Das Einbinden der Dateien in den Treiber erfolgt über die SOURCES Datei (Fußnote 16). Dazu wird dem Wert SOURCES der Name des Ressource Control Scripts hinzugefügt. Da das Build Tool den Message Compiler nicht automatisch aufruft, muß der Aufruf von Hand erfolgen, wenn sich das Definitionsfile geändert hat.
4.6.4 Registry Einträge
Als nächstes muß der Treiber als Event Source (Ereignisquelle) im System registriert werden. Dies erfolgt über mehrerer Registry Einträge. Dem Wert Sources im nachfolgend angegebenen Schlüssel muß der Name des Treibers ohne Dateiendung hinzugefügt werden.
Registry Schlüssel:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\EventLog\System P>
Hier wird auch ein neuer Schlüssel angelegt, der ebenfalls nach dem Treiber benannt wird. In diesem Schlüssel werden zwei neue Werte eingetragen: als erstes der Wert EventMessageFile vom Typ REG_EXPAND_SZ, der die Namen (inklusive Pfadangabe) der verwendeten Message Dateien enthält. Sollte der Treiber nur eigene Meldungen verwenden, muß hier nur der Treiber selbst eingetragen werden (Fußnote 17), andernfalls müssen die anderen Dateien, mit Semikola voneinander getrennt, hinzugefügt werden. Der nächste Wert TypesSupported vom Type REG_WORD gibt eine Bitmaske für die verwendeten Arten der Meldungen an. Normalerweise wird hier 0x7 eingetragen, was alle Arten einschließt.
4.6.5 Erzeugen einer Eventlog Meldung
Nachdem alle Vorarbeiten abgeschlossen sind, kann der Treiber nun Meldungen im Eventlog erzeugen. Dazu sind folgende Schritte notwendig:
Das IO_ERROR_LOG_PACKET besteht aus einem Header, einem Datenfeld, dessen Länge vom Treiber festgelegt wird, und einem oder mehreren nullterminierten Unicode Strings. Die Abbildung 4.6.5-1 beschreibt den Aufbau genauer.
Abbildung 4.6.5-1: Das IO_ERROR_LOG_PACKET
Die Größe des Pakets, die der Funktion IoAllocateErrorLogEntry als zweiter Parameter übergeben werden muß, kann mit Hilfe folgender Formel berechnet werden:
PaketGroesse =
sizeof(IO_ERROR_LOG_PACKET) +
DumpDataSize +
sizeof(InsertionStrings)
Wobei DumpDataSize die Größe des Datenfelds angibt und InsertionStrings alle zu übergebenden Strings enthält.
Als ersten Parameter der Funktion IoAllocateErrorLogEntry gibt man entweder ein Geräte- oder ein Treiberobjekt an, je nachdem, was am besten zu der Meldung paßt.
Wenn das IO_ERROR_LOG_PACKET erfolgreich reserviert werden konnte, werden die entsprechenden Daten in die Felder eingetragen. Zum Abschluß wird die Funktion IoWriteErrorLogEntry aufgerufen, die als einzigen Parameter einen Zeiger auf das IO_ERROR_LOG_PACKET übergeben bekommt. Danach schreibt der Logging Thread den Eintrag in das Eventlogfile und gibt anschließend den für das Paket reservierten Speicherplatz wieder frei (Fußnote 18).
4.7 Betriebssystemfunktionen im Kernelmodus
Windows NT stellt im Kernelmodus eine große Anzahl verschiedener Funktionen zur Verfügung. Abhängig von dem Modul, das die Funktionen zur Verfügung stellt, kann man mehrere Kategorien unterscheiden. Die Tabelle 4.6.5-1 gibt einen groben Überblick über die vorhandenen Funktionen und Kategorien.
Kategorie |
Funktionen für ... |
Funktionsnamen |
Ausführungsschicht (Executive) |
Speicherreservierung, Interlocked Queues, Lookaside Lists, System Worker Threads |
ExXxx() |
Hardware Abstraktionsschicht (Hardware Abstraction Layer – HAL) |
Zugriff auf Geräteregister, Buszugriffe |
HalXxx() |
I/O Manager |
Generelle Treiberunterstützung |
IoXxx() |
Kernel |
Synchronisation, DPCs |
KeXxx() |
Speicher Manager |
virtuelles auf physikalisches Mapping, Speicherreservierung |
MmXxx() |
Objekt Manager |
Management von Handles |
ObXxx() |
Process Manager |
Management von System Threads |
PsXxx() |
Laufzeitbibliothek |
String Manipulation, Arithmetikfunktionen, Zugriff auf die Registry, Sicherheitsfunktionen, Zeit- und Datumsfunktionen, Queue und Listenfunktionen |
RtlXxx() (meist) |
Sicherheitsüberwachung |
Privilegierungsüberprüfung, Security descriptor Funktionen |
SeXxx() |
Alle |
Interne Systemdienste |
ZwXxx() |
Tabelle 4.6.5-1: Betriebssystemfunktionen im Kernelmodus (aus [Baker97])