4.4 Linux für eingebettete Systeme
251
MMU entwickelt. Seit der Kernversion 2.5.45 ist uClinux integraler Bestandteil des
regulären Linux-Kerns für verschiedene Prozessorarchitekturen wie ARM7TDMI,
ARM Cortex-M3/4/7/R, MIPS, M68k/ColdFire sowie FPGA-basierte Prozessoren
wie Altera Nios II, Xilinx MicroBlaze und Lattice Mico 32.
Das Fehlen einer Speicherverwaltungseinheit in uClinux-basierten Systemen
bringt eine Reihe von Nachteilen mit sich. Eine direkte Folge ist der fehlende
Speicherschutz, somit kann jeder Prozess beliebig den Speicher anderer Prozesse auslesen und beschreiben. Das Fehlen einer MMU hat aber auch Folgen für die
Unix-eigene Methode zur Erzeugung neuer Prozesse. Normalerweise werden Prozesse in Unix als eine Kopie (Kindprozess) eines existierenden Prozesses durch
den fork()-Systemaufruf erstellt [470]. Dieser erzeugt aus Effizienzgründen keine
komplette Kopie des Speicherinhalts des aufrufenden Prozesses, sondern repliziert
nur dessen Seitentabelleneinträge, die im Kindprozess auf dieselben physikalischen
Seitenrahmen zeigen wie im aufrufenden Elternprozess. Wenn nun der Kindprozess
Daten in seinen Speicher schreibt, wodurch sich sein Speicherinhalt von dem des
Elternprozesses unterscheidet, werden nur die davon betroffenen Seitenrahmen mit
einer copy-on-write-Strategie kopiert. Die fehlende Unterstützung für die copy-onwrite-Semantik in MMU-losen Systemen und der in Folge benötigte hohe Aufwand,
bei Aufruf von fork() alle Seiten des Elternprozesses zu kopieren, führen dazu,
dass fork() in uClinux nicht zur Verfügung steht.
Stattdessen stellt uClinux den vfork()-Systemaufruf zur Verfügung. Dieser Systemaufruf nutzt aus, dass die meisten Unix-Prozesse direkt nach ihrer Erzeugung den
Systemaufruf exec() verwenden, um ihren Speicherinhalt durch den einer anderen
ausführbaren Datei zu ersetzen:
pid_t kindPID;
kindPID = vfork();
if (kindPID == 0) { // im Kindprozess
exec((/bin/shh, ,shh, 0);
}
printf((EEternprozess säuft wieder, PID des Kindprozesses ist %dd, kindPID);
Der direkte Aufruf von exec() nach fork() bedingt, dass der Inhalt des gesamten Adressraums des neu erzeugten Prozesses auf jeden Fall ersetzt wird und nur
der kleine Teil des Kindprozesses tatsächlich ausgeführt wird, der exec() aufruft.
Verglichen mit dem Standardverhalten von Unix garantiert vfork, dass der Elternprozess nach dem Aufruf von fork so lange angehalten wird, bis der Kindprozess
exec() aufgerufen hat. Dadurch ist der Elternprozess nicht dazu in der Lage, die
Ausführung des Kindprozesses zu beeinflussen (z.B. durch Schreibvorgänge), bis
dessen neuer Speicherinhalt geladen wurde. Zur sicheren Verwendung von vfork()
müssen allerdings einige Einschränkungen beachtet werden. Es ist nicht erlaubt,
den Stack des Kindprozesses zu verändern, d.h. vor exec dürfen keine Funktionsaufrufe erfolgen. Eine Folge davon ist, dass eine Rückkehr aus dem vfork-Aufruf
im Falle eines Fehlers, z.B. bei zu geringem Speicher oder einem Fehler bei der
Ausführung des neuen Programms, unmöglich ist, da dies den Stack verändern würde. Stattdessen lautet die Empfehlung, im Fehlerfall den Kindprozess mit Hilfe des
exit()-Systemaufrufs zu beenden.
Précédent

- 271/485

Suivant