Frage zu waitpid()
-
Hi
Ich habe eine Frage zu der Funktion waitpid()
Mein Problem ist folgendes:
Ich habe 5 Kindprozesse gestartet, die alle irgendetwas tun sollen. Deren PID hab ich in einem pid_t array abgespeichert. also pid[0]-pid[4]
Jetzt möchte ich im Elternprozess mittels waitpid zyklisch abfragen ob eines der fünf Kinder fertig ist. Also mittels endlosschleife und dem waitpid FLAG WNOHANG.
Aber wie kann ich herausfinden, ob ALLE fünf Kindprozesse fertig sind und so eine entsprechende Abbruchbedingung schreiben, damit ich die Endlosschleife im Elternprozess verlassen kann.
Gibt es dafür irgendeine Funktion oder wie kann man soetwas realisieren?
-
Hallo,
aus den Manpage:
If waitpid() function was invoked with WNOHANG set in options, it has
at least one child process specified by pid for which status is not
available, and status is not available for any process specified by
pid, a value of zero is returned. Otherwise, a value of −1 is returned,
and errno is set to indicate the error.Und im nicht Fehlerfalle:
If wait()or waitpid() returns due to a stopped or terminated child pro‐
cess, the process ID of the child is returned to the calling process.
Otherwise, a value of −1 is returned and errno is set to indicate the
error.gruss
v R
-
hab mal was versucht aber irgendwie klappt es nicht... hier das dazugehörige codestück
for(i=0; i < counter; i++) { pidsave[i] = fork(); switch(pidsave[i]) { case -1: fprintf(stderr, "%s: Fehler bei fork()!\n", argv[0]); return EXIT_FAILURE; case 0: child(content, i); break; default: for(i=0; ; i++) { if((wpid = waitpid(pidsave[i], &childstatus, WNOHANG)) < 1) { if(wpid == -1) { fprintf(stderr, "%s: Fehler bei waitpid!\n", argv[0]); return EXIT_FAILURE; } else { if(childstatus == 0) { j++; continue; } else return EXIT_FAILURE; } } if( j == counter) break; if( i >= counter) i=0; } } }
in counter steht drinnen wieviele kindprozesse es geben soll. In meinem Beispiel derzeit 5. Speicherplatz für pidsave wurde vorher entsprechend reserviert. die child() Funktion führt einfach immer einen speziellen exec() befehl aus.
Danach will ich im Elternprozess einfach periodisch Abfragen ob eines der Kinder fertig ist (deshalb die Endlosschleife). Wenn der childstatus = 0 ist, ist meines Wissens ja ein Kind beendet. Deshalb erhöhe ich hier einen Zähler. Wenn der Zähler 5 ist (eben die Zahl die in counter steht) wird die Endlosschleife verlassenSoweit meine Gedanken dazu. Allerdings wird in der Ausführung des Programms nur ein Kindprozess erzeugt und mit dem entsprechenden exec Befehl überlagert. Kommentier ich den kompletten default Zweig aus werden alles 5 Kindprozesse abgehandelt. Nur brauche ich eben mittels waitpid im Elternprozess die Kontrolle. Also waitpid.
-
Dann setze die Endlosschleife doch hinter die Schleife, die die fünf Prozesse startet, und nicht in den Default-Zweig, der nach dem Starten des ersten Prozesses ausgeführt wird.
-
in diesem fall solltest du eher das signal SIGCHLD verwenden. sobald sich der status eines kindprozesses ändert, wird diese signal an den elternprozess geschickt. im signal handler muss wait aufgerufen werden. sobald der handler fünf mal aufgerufen wurde, sind alle kindprozesse beendet worden.
-
die idee ist gut.. habe mal was versucht hierzu, bin mir aber nicht sicher ob das richtig funktioniert (arbeite zum ersten mal mit signalen)
Also die Ausgabe mit Kindern beendet usw bekomme ich, allerdings geht das Programm danach noch irgendwie weiter. Die Kidner werden mittels exec() befehl mit wget überlagert. vielleicht liegts auch am wget befehlHier die main funktion
for(i=0; i < filecount; i++) { pidsave[i] = fork(); switch(pidsave[i]) { case -1: fprintf(stderr, "%s: Fehler bei fork()!\n", argv[0]); return EXIT_FAILURE; case 0: child(filecontent, i); break; default: (void) signal(SIGCLD, j=parentwait(j, filecount, pidsave, childstatus) ); if (j == filecount) printf("ALLE KINDER BEENDET"); } }
Hier die parentwait() Funktion
int parentwait(int count, int filecount, pid_t *pidsave, int status) { pid_t wpid; int i; for(i=0; i < filecount; i++) { if((wpid = waitpid(pidsave[i], &status, WNOHANG)) < 1) { if(wpid == -1) { fprintf(stderr, "Fehler bei waitpid!\n"); return EXIT_FAILURE; } else { if(status == 0) { printf("KIND BEENDET\n"); count++; continue; } else return EXIT_FAILURE; } } } return count; }
Also die parentwaitfunktion geht einfach jede pid durch und schaut mittels waitpid welches der kinder terminiert hat. wenn sie eins gefunden hat wird die variable count um eins erhöht und zurückgegeben.
Im default Zweig in main() wird ein einfaches printf ausgegeben wenn alle Kinder fertig sind. Sprich die Variable j den Wert der Anzahl der Kinder hat (steht in filecount drinnen)
-
Hi
guck dir die man Page zu man: waitpid(2). Es gibt mehrere Makros, mit denen du den Inhalt der status Variable (2. Parameter von waitpid) prüfen kannst und die solltes du auch verwenden. Also wäre deine Funktion besser so:
if((wpid = waitpid(pidsave[i], &status, WNOHANG)) < 1) { if(wpid == -1) { fprintf(stderr, "Fehler bei waitpid!\n"); return EXIT_FAILURE; } else { if(WIFEXITED(status) == 0) { printf("KIND BEENDET\n"); count++; continue; } else return WEXITSTATUS(status); /* return Fehlercode des Kindes */ } }
-
bin auf noch ein Problem gestoßen
in main sieht der code jetzt so aus
for(i=0; i < filecount; i++) { pidsave[i] = fork(); switch(pidsave[i]) { case -1: fprintf(stderr, "%s: Fehler bei fork()!\n", argv[0]); return EXIT_FAILURE; case 0: child(filecontent, i, argv); break; default: (void) signal(SIGCLD, parentwait ); } }
d.h wenn der Elternprozess SIGCLD bekommt ruft er parentwait auf
void parentwait(int sig) { pid_t wpid; while(1) { if((wpid = waitpid(pidsave[i], &childstatus, WNOHANG)) < 1) { if(wpid == -1) { fprintf(stderr, "Fehler bei waitpid!\n"); exit (EXIT_FAILURE); } else { if(WIFEXITED(childstatus) == 0) { printf("KIND %d BEENDET \n", pidsave[i]); break; } else { fprintf(stderr, "KIND %d mit Fehler oder durch Signal beendet\n", pidsave[i]); break; } } } } }
die variablen in parentwait sind global da man ja anscheinend dem signalhandler keine variablen übergeben kann (bekam dauernd Fehler und Warnings wenn ich es versucht habe)
Wenn ich jetzt mein Programm ausführe wird im Kindprozess wget mittels exec ausgeführt. Und dann erhalte ich immer die Meldung "Fehler bei waitpid!" für jedes Kind... Jetzt frage ich mich natürlich wieso. Wieso liefert mir waitpid immer -1 als Rückgabewert? hat jemand eine Erklärung dafür?
-
verwende nicht pidsave[i] sondern -1. damit bekommst du entweder 0 zurück, wenn kein kind beendet wurde, oder die pid des kindes, das beendet wurde. sobald sigchld aufgerufen wird, wurde mindestens ein kind beendet. es können aber auch mehrere sein. deshalb passt das mit der schleife gut. verwende statt signal lieber sigaction. es ist das modernere interface. und sigwait anstelle von pause(), falls du es verwendest, um auf die signale zu warten.
-
habe deinen rat beherzigt und bei waitpid jetzt -1 statt pidsave[i] genommen. allerdings erhalte ich trotzdem insgesamt 4mal die Meldung "Fehler bei waitpid!" sprich 4mal liefert mir waitpid -1 zurück
(es werden derzeit in meinem programm 4 Child Prozesse erzeugt, deshalb 4mal die Meldung)
hab echt ka wieso das nicht klappt
-
lass dir per strerror oder perror die fehlermeldung ausgeben.
was anderes: hast du schon mal an die libcurl gedacht? mit der kannst du auch problemlos dateien runterladen und musst nicht forken, was weitaus professioneller ist.
-
ich erhalte als Fehlermeldung
waitpid: No child Processesmhm kann ich mir nicht erklären, da nur im childprocess wget ausgeführt wird und ich in der bash sehe wie wget, die angegebenen Seiten herunterlädt..
das ganze mit fork und wget zu lösen ist leider eine Anforderung an das Programm, deshalb kann ichs nicht mit deinem vorgeschlagenem Befehl versuchen.
-
Naja, man: waitpid gibt halt -1 zurück und setzt errno auf ECHILD, wenn es keinen beendeten Kindprozess gab. Also kannst du den Fehler einfach ignorieren.
Schau doch einfach in die Doku man: waitpid(2).
btw. siehe auch http://www.c-plusplus.net/forum/viewtopic-var-p-is-1479904.html#1479904
-
sorry, aber wo liest du das? ich muss das übersehen haben. die manpage sagt "if WNOHANG was specified and no child(ren) specified by pid has yet changed state, then 0 is returned." anscheinend stimmt das für -1 als wert in für pid nicht. das scheint mir, eine inkonsistenz zu sein.