Es ist wichtig zu beachten, dass sich die Kernel-Programmierung erheblich von der Userspace-Programmierung unterscheidet. Der Kernel ist eine eigenständige Entität, die keine Userspace-Bibliotheken verwenden kann, nicht einmal libc unter Linux oder kernel32.dll Windows. Dadurch können die im Userspace üblichen Funktionen (printf, malloc, free, open, read, write, memcpy, strcpy, etc.) nicht mehr genutzt werden.
Zusammenfassend lässt sich sagen, dass die Kernel-Programmierung auf einer völlig neuen und unabhängigen API basiert, die nichts mit der API im Userspace zu tun hat, egal ob wir uns auf POSIX beziehen , Win32 oder ANSI C. Ein wichtiger Unterschied in der Kernel-Programmierung besteht darin, wie man darauf zugreift. und Speicherbelegung. Aufgrund der Tatsache, dass die Kernel-Programmierung auf einer Ebene sehr nahe an der physischen Maschine erfolgt, gibt es wichtige Regeln bezüglich der Speicherverwaltung.
Erstens funktioniert es mit mehreren Arten von Speicher:
– physikalischer Speicher
– virtueller Speicher im Kernel-Adressraum
– virtueller Speicher aus dem Adressraum eines Prozesses
– residenter Speicher – wir wissen mit Sicherheit, dass die aufgerufenen Seiten im physischen Speicher vorhanden sind
Der virtuelle Speicher im Adressraum eines Prozesses kann aufgrund der vom Betriebssystem implementierten virtuellen Speichermechanismen nicht als resident betrachtet werden: Die Seiten können sich im Swap befinden oder nicht vorhanden sein im physischen Speicher als Ergebnis des Anforderungs-Paging-Mechanismus.
Der Speicher im Kernel-Adressraum kann resident sein oder nicht. Sowohl die Daten- als auch die Codesegmente eines Moduls und der Kernel-Stack eines Prozesses sind resident.
Dynamischer Speicher kann resident sein oder nicht, je nachdem, wie er zugewiesen wird. Beim Arbeiten mit residentem Speicher sind die Dinge einfach: Auf den Speicher kann jederzeit zugegriffen werden. Wenn Sie jedoch mit nicht-residentem Speicher arbeiten, kann nur von bestimmten Kontexten aus darauf zugegriffen werden.
Aufnicht residenten Speicher kann nur aus dem Prozesskontext zugegriffen werden. Der Zugriff auf den nicht-residenten Speicher aus dem Interrupt-Kontext hat unvorhersehbare Folgen und daher ergreift das Betriebssystem drastische Maßnahmen, wenn es einen solchen Zugriff erkennt: Blockieren oder Zurücksetzen des Systems, um ernsthafte Beschädigungen zu verhindern.
Auf den virtuellen Speicher eines Prozesses kann nicht direkt vom Kernel aus zugegriffen werden. Im Allgemeinen wird davon abgeraten, auf den Adressraum eines Prozesses zuzugreifen, aber es gibt Situationen, in denen ein Gerätetreiber dies tun muss. Der typische Fall ist, dass der Gerätetreiber auf einen Puffer aus dem Userspace zugreifen muss. In diesem Fall muss der Gerätetreiber spezielle Funktionen verwenden und nicht direkt auf den Puffer zugreifen. Dies ist notwendig, um den Zugriff auf ungültige Speicherbereiche zu verhindern.
Ein weiterer Unterschied zur Programmierung im Userspace, relativ zur Arbeit mit Speicher, liegt am Stack, dem Stack, dessen Größe fest und begrenzt ist. Im Linux-Kernel wird standardmäßig ein 4K-Stack und in Windows ein 12K-Stack verwendet. Aus diesem Grund sollte die Allokation großer Strukturen auf dem Stack oder die Verwendung rekursiver Aufrufe vermieden werden.
Bezüglich des Ausführungsmodus im Kernel unterscheiden wir zwei Kontexte: Prozesskontext und Interruptkontext. Wir befinden uns im Prozesskontext, wenn wir Code nach einem Systemaufruf ausführen oder wenn wir im Kontext eines Kernel-Threads ausgeführt werden. Wenn wir in der Routine des Umgangs mit einer Unterbrechung oder einer verzögerten Aktion ausgeführt werden, befinden wir uns in einem Unterbrechungskontext.
Eine der wichtigsten Eigenschaften der Kernel-Programmierung ist die Parallelität. Sowohl Linux als auch Windows unterstützen SMP-Systeme mit mehreren Prozessoren, aber auch der Kernel unterstützt präventiv mehrere Prozessoren. Dies erschwert die Kernel-Programmierung, da der Zugriff auf globale Variablen entweder mit Spinlock- oder blockierenden Primitiven synchronisiert werden muss.
Sowohl Linux als auch Windows verwenden preemptive Kernel. Der Begriff des präemptiven Multitasking sollte nicht mit dem Begriff des präemptiven Kernels verwechselt werden. Der Begriff des präventiven Multitasking bezieht sich auf die Tatsache, dass das Betriebssystem die Ausführung eines Prozesses zwangsweise unterbricht, wenn er die Zeit abgelaufen ist und im Userspace läuft, um einen anderen Prozess auszuführen.
Für die Programmierung im Linux-Kernel, die Konvention, die zum Aufrufen von Funktionen verwendet wird, um Erfolg anzuzeigen, ist identisch mit der in der UNIX-Programmierung: 0 für Erfolg oder ein anderer Wert als 0 für Fehler.