Ich hatte mal eine Anleitung dazu geschrieben, wie mittels fork und exec Kind-Prozesse erstellt und Programme ausgeführt werden. In diesem Artikel möchte ich die Funktion posix_spawn vorstellen, die im Prinzip das gleiche kann und dabei ein paar Vorteile hat.
In Unix werden traditionell Programme ausgeführt, in dem der aktuelle Prozess geforked, also dupliziert, wird und im neuen Kind-Prozess wird ein anderes Programm-Image mittels exec geladen.
Das Problem an der Sache ist, dass fork mehr oder weniger Overhead haben kann, je nach Betriebsystem. Linux ist da recht effizient, es werden aber immer noch die Page Tables des Parent kopiert. Dazu kommt, dass es auch etwas umständlicher zu programmieren ist, als nur eine einzige Funktion aufzurufen.
Daher wurde irgendwann die Funktion posix_spawn eingeführt:
int posix_spawn(pid_t *pid, const char *path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp,
char *const argv[], char *const envp[]);
Um z.B. das Programm /bin/echo in einem separaten Prozess auszuführen, reicht folgender Code:
char *args[3];
args[0] = "echo";
args[1] = "Hello World";
args[2] = NULL;
pid_t pid; // child pid
posix_spawn(&pid, "/bin/echo", NULL, NULL, args, NULL);
Das ist ein sehr einfaches Beispiel. Was man eigentlich noch bräuchte, ist eine Möglichkeit, die Filedescriptoren für stdin, stdout und stderr zu modifizieren. Hierfür gibt es den Parameter file_action. Um den zu verstehen, sollte man sich noch mal anschauen, wie Programme mittels fork und exec ausgeführt werden, daher noch mal eine kleine Zusammenfassung meines Artikels dazu.
Stdin, stdout und stderr sind Filedescriptoren, die immer die Nummern 0, 1 und 2 haben. Mit der Funktion dup2 können wir einen Filedescriptor duplizieren und der Kopie eine exakte Nummer zuweisen. Was man also machen muss ist, vor dem Erstellen des neuen Prozesses erstmal neue Filedescriptoren für stdin/stdout/stderr zu erstellen (z.B. mit pipe oder open) und nach dem Erstellen des Kind-Prozesses aber noch vor exec werden mittels dup2 die Filedescriptoren auf die Positionen 0, 1 und 2 gelegt.
Der file_actions Parameter bei posix_spawn ist genau dafür gedacht. Diesem können Aktionen wie dup2 oder auch close und open zugeordnet werden, die dann nach dem Erstellen des Kind-Prozesses verarbeitet werden.
Zuerst muss file_actions hierfür initialisiert werden:
posix_spawn_file_actions_t actions;
posix_spawn_file_actions_init(&actions);
Danach können Actions zugewiesen werden:
// stderr im Kind-Prozess schließen
posix_spawn_file_actions_addclose(&actions, 2);
// stdout in die Datei out.txt umleiten
posix_spawn_file_actions_addopen(&actions, 1, "out.txt", O_CREAT|O_WRONLY, 0644);
Nach dem Ausführen von posix_spawn sollte der Speicher wieder freigegeben werden:
posix_spawn_file_actions_destroy(&actions);
Ich habe auch ein ausführliches Beispiel geschrieben, in dem die Ausgabe in eine Pipe mit Hilfe von posix_spawn_file_actions_adddup2 umgeleitet wird, die dann im Parent ausgelesen wird.
Kommentare