Beispiellösung
Content |
Antworten
-
Mit den Funktionen sighold und sigrelse lässt sich das Signal SIGTERM hinreichend lange blockieren. Dabei ist aber zu beachten, dass die Blockierung an drei Stellen aufgehoben werden muss: für den aufrufenden Prozess, für \(P_0\) nach der Einrichtung des Signalbehandlers für SIGTERM und für \(P_1, \dots, P_{n-1}\) unmittelbar nach der Erzeugung.
-
In der Schleife müssen SIGTERM und SIGUSR1 blockiert werden und sigsuspend wird so aufgerufen, dass die Blockierung der beiden Signale während des Wartens aufgehoben wird.
Implementierung
#include <signal.h> #include <stdlib.h> #include <stdio.h> #include <sys/wait.h> #include <unistd.h> volatile unsigned int have_token = 0; volatile unsigned int signal_count = 0; volatile unsigned int terminate = 0; void sighandler(int signo) { have_token = 1; ++signal_count; } void termination_handler(int signo) { terminate = 1; } void run_member_of_ring(pid_t next) { sigset_t empty_set; sigemptyset(&empty_set); for(;;) { sighold(SIGTERM); sighold(SIGUSR1); if (!have_token && !terminate) { sigsuspend(&empty_set); } sigrelse(SIGTERM); sighold(SIGUSR1); if (terminate) break; if (kill(next, SIGUSR1) < 0) break; have_token = 0; } } pid_t create_tokenring(unsigned int members) { pid_t master = fork(); sighold(SIGTERM); if (master < 0) return master; if (master == 0) { struct sigaction sigact1 = { .sa_handler = sighandler, }; if (sigaction(SIGUSR1, &sigact1, 0) < 0) { exit(255); } master = getpid(); setpgid(master, 0); pid_t next = master; for (unsigned int member = 1; member < members; ++member) { pid_t child = fork(); if (child < 0) { exit(255); } if (child == 0) { sigrelse(SIGTERM); run_member_of_ring(next); exit(0); } setpgid(child, master); next = child; } struct sigaction sigact2 = { .sa_handler = termination_handler, }; if (sigaction(SIGTERM, &sigact2, 0) < 0) { exit(255); } sigrelse(SIGTERM); have_token = 1; run_member_of_ring(next); /* wait for all childs */ int wstat; while (waitpid(-master, &wstat, 0) > 0) { } printf("%u rounds\n", signal_count); exit(0); } sigrelse(SIGTERM); return master; } int main() { pid_t master = create_tokenring(3); sleep(1); kill(-master, SIGTERM); int stat; waitpid(master, &stat, 0); }
theon$ gcc -Wall -o tokenring2 tokenring2.c theon$ ./tokenring2 6060 rounds theon$
Beachten Sie, dass unter Linux die Funktionen sighold und sigrelse anders als bei Solaris nicht per Voreinstellung in den entsprechenden Headern deklariert werden, solange nicht ein entsprechender POSIX-Standard eingefordert wird:
heim$ gcc -Wall -o tokenring2 tokenring2.c tokenring2.c: In function 'run_member_of_ring': tokenring2.c:24:7: warning: implicit declaration of function 'sighold'; did you mean 'strtold'? [-Wimplicit-function-declaration] sighold(SIGTERM); sighold(SIGUSR1); ^~~~~~~ strtold tokenring2.c:28:7: warning: implicit declaration of function 'sigrelse'; did you mean 'sigdelset'? [-Wimplicit-function-declaration] sigrelse(SIGTERM); sighold(SIGUSR1); ^~~~~~~~ sigdelset heim$ gcc -Wall -D_XOPEN_SOURCE=600 -o tokenring2 tokenring2.c heim$ ./tokenring2 27708 rounds heim$