Wie warte ich richtig auf Vordergrund- / Hintergrundprozesse in meiner eigenen Shell in C?

8

In diese vorherige Frage habe ich am meisten gepostet meines eigenen Shell-Codes. Mein nächster Schritt besteht darin, die Ausführung von Vordergrund- und Hintergrundprozessen zu implementieren und richtig darauf zu warten, dass sie beendet werden, damit sie nicht als "Zombies" bleiben.

Vor dem Hinzufügen der Möglichkeit, sie im Hintergrund auszuführen, wurden alle Prozesse im Vordergrund ausgeführt. Und dafür habe ich nach dem Ausführen eines Prozesses mit execvp () einfach wait (NULL) aufgerufen. Jetzt schaue ich nach dem '& amp;' Zeichen als letztes Argument und wenn es da ist, führe den Prozess im Hintergrund aus, indem ich nicht wait (NULL) anrufe und der Prozess kann glücklich im Hintergrund laufen, wenn ich zu meiner Shell zurückkomme.

Das funktioniert alles richtig (denke ich), das Problem jetzt ist, dass ich auch wait () (oder waitpid ()?) irgendwie aufrufen muss, damit der Hintergrundprozess nicht "Zombie" bleibt. Das ist mein Problem, ich bin mir nicht sicher, wie ich das machen soll ...

Ich glaube, ich muss mit SIGCHLD umgehen und etwas dort tun, aber ich muss noch vollständig verstehen, wenn das SIGCHLD-Signal gesendet wird, weil ich versucht habe, wait (NULL) zu childSignalHandler () hinzuzufügen, aber es hat nicht funktioniert, weil Sobald ich einen Prozess im Hintergrund ausgeführt habe, wurde die childSignalHandler () -Funktion aufgerufen und folglich das wait (NULL), was bedeutet, dass ich nichts mit meiner Shell machen konnte, bis der "Hintergrund" -Prozess beendet war. Die wegen der Wartezeit im Signalhandler nicht mehr im Hintergrund lief.

Was vermisse ich in all dem?

Noch eine letzte Sache, Teil dieser Übung Ich muss auch die Änderungen des Prozesses Status, wie Prozess Beendigung drucken. Also, jeder Einblick darauf ist auch sehr geschätzt.

Dies ist im Moment mein vollständiger Code:

%Vor%     
Ricardo Amaral 22.05.2009, 23:27
quelle

4 Antworten

4

Es gibt verschiedene Optionen für waitpid() , die Ihnen helfen (Zitate aus dem POSIX-Standard):

  

WCONTINUED

     

Die Funktion waitpid () soll den Status eines fortgesetzten untergeordneten Prozesses melden, der von pid angegeben wurde, dessen Status nicht gemeldet wurde, seit er von einem Jobsteuerungsstopp fortgesetzt wurde.

     

WNOHANG

     

Die Funktion waitpid () darf die Ausführung des aufrufenden Threads nicht anhalten, wenn der Status nicht sofort für einen der von pid angegebenen untergeordneten Prozesse verfügbar ist.

Insbesondere können Sie mit WNOHANG sehen, ob Leichen gesammelt werden, ohne dass Ihr Prozess das Warten auf eine Leiche blockiert.

  

Wenn der aufrufende Prozess SA_NOCLDWAIT gesetzt hat oder SIGCHLD auf SIG_IGN gesetzt ist, und der Prozess nicht unbelegt ist - für Kinder, die in Zombie-Prozesse transformiert wurden, sollte der aufrufende Thread bis zu allen Kindern des Prozesses blockieren, der den aufrufenden Thread enthält Beenden, und wait () und waitpid () werden fehlschlagen und errno auf [ECHILD] setzen.

Sie wollen wahrscheinlich nicht SIGCHLD usw. ignorieren, und Ihr Signalhandler sollte wahrscheinlich eine Markierung setzen, um Ihrer Hauptschleife zu sagen "Hoppla; da ist totes Kind - geh und sammle diese Leiche!".

Auch die Signale SIGCONT und SIGSTOP sind für Sie relevant - sie dienen zum Neustart bzw. zum Beenden eines Kindprozesses (in diesem Zusammenhang jedenfalls).

Ich würde empfehlen, Rochkinds Buch oder Stevens 'Buch anzuschauen - sie behandeln diese Fragen im Detail.

    
Jonathan Leffler 22.05.2009, 23:53
quelle
2

Damit sollten Sie beginnen. Der Hauptunterschied ist, dass ich den Kind-Handler losgeworden bin und waitpid in der Hauptschleife mit etwas Feedback hinzugefügt habe. Getestet und funktioniert, benötigt aber offensichtlich mehr TLC.

%Vor%

BEARBEITEN: Das Zurücksetzen der Signalverarbeitung ist nicht schwierig mit waitpid() unter Verwendung von WNOHANG. Es ist so einfach, den waitpid() -Stuff vom Anfang der Schleife in den Signal-Handler zu verschieben. Sie sollten jedoch zwei Dinge beachten:

Erstens werden sogar "Vordergrund" -Prozesse SIGCHLD senden. Da es nur einen Vordergrundprozess geben kann, können Sie einfach die Vordergrund-PID (Eltern-Rückgabewert von fork() ) in einer Variablen speichern, die für den Signal-Handler sichtbar ist, wenn Sie eine spezielle Behandlung von Vordergrund und Hintergrund vornehmen möchten.

Zweitens blockieren Sie derzeit I / O auf der Standardeingabe ( read() bei Hauptschleife oben). Wenn SIGCHLD auftritt, ist es sehr wahrscheinlich, dass Sie bei read() blockiert sind, was zu einem unterbrochenen Systemaufruf führt. Je nach Betriebssystem kann es den Systemaufruf automatisch neu starten oder ein Signal senden, das Sie behandeln müssen.

    
dwc 23.05.2009 00:12
quelle
2

Sie können verwenden:

%Vor%

Auf diese Weise blockiert der Prozess, bis er das Signal SIGCHLD empfängt, und der Signal-Handler erledigt die Wartezeit.

    
aaa 07.05.2011 09:58
quelle
0

Anstatt eine globale Variable zu verwenden, dachte ich an eine andere Lösung:

%Vor%

Wenn ich einen Vordergrundprozess ausführe, "lösche" den Handler für SIGCHLD, damit er nicht aufgerufen wird. Setzen Sie nach waitpid () den Handler erneut. Auf diese Weise werden nur die Hintergrundprozesse behandelt.

Glauben Sie, dass mit dieser Lösung etwas nicht stimmt?

    
Ricardo Amaral 24.05.2009 16:24
quelle