Zombieprozesse vermeiden
-
Hallo,
ich schreibe zur Zeit an einem Kommandointerpreter. Bei Ausführung dessen, sollen Zombieprozesse (welche aus aufgerufene Kommandos resultieren) vermieden werden. Leider haut dies nicht so ganz hin.
Mit folgender kopfgesteuerten Schleife versuche ich diese zu verhindern
while ((w = waitpid(-1, &status, WNOHANG)) > 0) { if (w < 0) perror("WaitPID"); else sleep(1); }
Mit "ps x" rufe ich mir die Prozesstabelle auf. Dort taucht dann das nervige "defunct" vor dem Prozess (ein C-Programm das 5 Sekunden schläft) auf. Das Ganze geschieht während des asynchronen Betriebes.
-
du hast als schleifenbedingung, dass w (bzw. der rückgabewert von waitpid()) größer als 0 sein muss. in der schleife drin, hast du eine bedingung für w kleiner als 0. das ist aber nicht der fehler.
das zweite ist, dass du größer 0 schreibst. das wird wohl der fehler sein. besser das mal in >= aus. mit option WNOHANG gibt waitpid 0 zurück, sollte kein status eines kindprozesses verfügbar sein. wenn du deine schleife beginnst, bevor ein kindprozess beendet wurde, wird die schleife nie ausgeführt, da beim ersten aufruf schon w gleich 0 ist.
du solltest aber sowieso auf aktives warten mit waitpid verzichten. hat das einen bestimmten grund, wieso du das machst?
-
Es ist Teil der Aufgabenstellung, dass Zombies (mit Hilfe von waitpid) vermieden werden müssen. Ich verstehe nicht ganz was du mit aktivem Warten meinst. Gibt es auch ein passives?
-
am einfachsten ist folgende Methode um Zombies zu vermeiden.
void sigchld(int s) { (void)s; while(waitpid(-1, 0, WNOHANG) > 0); } int main() { signal(SIGCHLD, sigchld); // ... danach forken }
-
aktives warten bedeutet, dass du eine bedigung immer wieder prüfst. das andere (passiv würde ich es jetzt nicht nennen *g*. eher blockierend.) bedeutet, dass der prozess vom os unterbrochen wird, bis die bedingung erfüllt ist.
wenn du das WNOHANG entfernst, wird waitpid solange blockieren, bis mindestens ein status eines kindprozesses verfügbar ist. du simulierst das warten auf einen status mit deiner while schleife, sofern du das > 0 durch >= 0 ersetzt, damit das richtig funktioniert. oder du machst das, was rüdiger geschrieben hat. aber da du anscheinend so eine art shell schreibst, ist waitpid ohne WNOHANG günstiger, da die shell ja sowieso auf das ende des kindprozesses warten muss, bevor sie wieder kommandos aufnehmen kann. signale eignen sich mehr für das asynchrone ausführen eines oder mehrer kindprozesse.
-
Habe vergessen in diesem Zusammenhang zu erwähnen, dass ich diese "Abräumschleife" zum vermeiden von Zombies verwende, welche während des asynchronen Aufrufs des Kommandos entstehen (also "kommando &"). Deswegen kann ich auf WNOHANG nicht verzichten.
-
dann verwende lieber die methode von rüdiger.
-
rüdiger schrieb:
am einfachsten ist folgende Methode um Zombies zu vermeiden.
void sigchld(int s) { (void)s; while(waitpid(-1, 0, WNOHANG) > 0); } int main() { signal(SIGCHLD, sigchld); // ... danach forken }
Falsch! Die einfachste Methode ist:
signal(SIGCHLD, SIG_IGN);
(Ohne eigenen Signal-Handler)
Übrigens: Wenn man schon eine "aktive" Warteschleife baut, sollte man statt sleep(1) besser sched_yield() aufrufen. Dann wartet der Prozess nicht eine Sekunde sondern gibt die Kontrolle wieder an den Scheduler (der die laufenden Prozesse nacheinander aufruft), bis er wieder an der Reihe ist.
-
Das mit dem SIG_IGN ist aber nicht POSIX kompatibel!