Organisation des Token-Rings als Prozessgruppe
Content |
Damit sich der Token-Ring leichter kontrollieren lässt, wäre es sinnvoll, hierfür eine Prozessgruppe anzulegen mit \(P_0\) als Repräsentanten der Prozessgruppe.
Zur Erinnerung: Ein Prozess (erster Parameter) wird mit dem Systemaufruf setpgid einer Prozessgruppe zugeordnet. Wenn der zweite Parameter 0 ist, wird eine neue Prozessgruppe erzeugt, die von dem im ersten Parameter identifizierten Prozess repräsentiert wird:
int setpgid(pid_t pid, pid_t pgid);
Prozessgruppen können nicht nur bei waitpid angegeben werden, sondern auch bei kill. Das ermöglicht es, ein Signal an die gesamte Prozessgruppe zu leiten.
Aufgabe
Entwickeln Sie Ihre Lösung dahingehend weiter, dass
-
der Prozess \(P_0\) zum Repräsentanten einer Prozessgruppe wird, der die Prozesse \(P_0, \dots, P_{n-1}\) angehören,
-
die Zahl der Runden nicht mehr begrenzt ist und stattdessen der Erzeuger von \(P_0\) eine Sekunde nach dem Aufruf von create_tokenring das Signal SIGTERM an die Prozessgruppe des Rings sendet,
-
keine Ausgabe mehr erfolgt, wenn ein Prozess ein Token erhält,
-
die Zahl der eingegangenen SIGUSR1-Signale gezählt wird und
-
\(P_0\) einen Signalbehandler für SIGTERM einrichtet, ohne diesen weiter zu vererben,
-
beim Eintreffen von SIGTERM \(P_0\) kontrolliert die Schleife beendet, während \(P_1, \dots, P_{n-1}\) ganz normal mit SIGTERM beendet werden und
-
\(P_0\) am Ende noch vor dem exit die Zahl der eingegangenen SIGUSR1-Signale ausgibt.
Fragen
-
Wenn der Hauptprozess nach der Rückkehr von create_tokenring SIGTERM an die Prozessgruppe schickt, könnte es sein, dass \(P_0\) den entsprechenden Signalbehandler noch nicht eingesetzt hat. Wie könnte dieses Fenster geschlossen werden?
-
Wie kann der Abbau der Prozessgruppe wohldefiniert erfolgen? Bedenken Sie, dass zwischen einer Abfrage, ob SIGTERM bereits eingegangen ist und dem Aufruf von pause möglicherweise SIGTERM eingeht und der Prozess anschließend in pause auf Dauer hängt, weil keine weiteren Signale kommen.
Um das Problem zu lösen, ist es sinnvoll, neben sighold und sigrelse auch die Funktion sigsuspend in Betracht zu ziehen, die wie pause funktioniert, aber mit der gegebenen Menge während des Wartens blockierter Signale arbeitet:
int sigsuspend(const sigset_t *mask);
Das kann dann etwa so genutzt werden, indem sigsuspend eine leere Maske erhält:
sighold(SIG...); if (/*...*/) { sigset_t empty_set; sigemptyset(&empty_set); sigsuspend(&empty_set); /* we can be interrupted by SIG... */ } sigrelse(SIG...);