passive.c
#include <stdlib.h> #include <stdio.h> #include <errno.h> #include <signal.h> #include <unistd.h> // Wichtig: volatile! // Sorgt dafür, dass der Wert bei jedem Zugriff neu aus dem Speicher gelesen wird. // Der Compiler weiß nicht, dass event nebenläufig verändert wird und // könnte andernfalls den Check in der while-Schleife wegoptimieren und // einmalig vor der Schleife ausführen. static volatile int event = 0; // Signalhandler für SIGUSR1 static void usr1handler(int signum) { // errno sichern, falls sie im Signalhandler verändert wird / werden könnte int errno_backup = errno; event = 1; // Zu Demonstrationszwecken Text ausgeben, wenn die Signalbehandlung läuft write(STDOUT_FILENO, "SIGNAL!\n", 8); // Wiederherstellen der errno errno = errno_backup; } int main(void) { // struct für die Signalbehandlung "SIGUSR1" vorbereiten struct sigaction action = { .sa_handler = usr1handler, .sa_flags = SA_RESTART, }; sigemptyset(&action.sa_mask); // Signalmaske, die SIGUSR1 blockiert, um Race-Conditions zu vermeiden. sigset_t usr1_set; sigemptyset(&usr1_set); sigaddset(&usr1_set, SIGUSR1); // Signalmaske während wir auf SIGUSR1 warten. Hier muss SIGUSR1 erlaubt sein. sigset_t wakeup; sigemptyset(&wakeup); // Alte Signalmaske, die wir uns speichern und später wiederherstellen können. sigset_t old; // Signalbehandlung für SIGUSR1 aktivieren sigaction(SIGUSR1, &action, NULL); // SIGUSR1 blockieren, um Race-Condition zwischen // if (event == 0) und Schlafenlegen zu beheben. // Die bis hier aktive Maske in "old" speichern, um sie später wiederherzustellen. sigprocmask(SIG_BLOCK, &usr1_set, &old); printf("Der Prozess schläft jetzt 10 Sekunden lang und hat SIGUSR1 blockiert.\n"); printf("Schick ihm doch trotzdem mal ein paar (pkill -USR1 passive)\n"); sleep(10); printf("Na, wie oft konntest du den Signalhandler in den 10 Sekunden triggern?\n"); printf("(Maximal 1x - das Signal ist blockiert und nur ein Vorkommen wird gepuffert!)\n"); while (event == 0) { // sigsuspend setzt die threadspezifische aktive Signalmaske auf *wakeup* // und legt den Prozess schlafen, bis ein in wakeup *nicht* blockiertes Signal // eintrifft. Beim Eintreffen eines nicht blockierten Signals wird der // Signalhandler aufgerufen. Nachdem der Signalhandler durchgelaufen ist, // kehrt sigsuspend zurück und stellt die vorherige threadspezifische aktive // Signalmaske wieder her (-> SIGUSR1 ist nach sigsuspend wieder blockiert). sigsuspend(&wakeup); } // Die alte threadspezifische Signalmaske wiederherstellen - in der SIGUSR1 // entweder erlaubt oder blockiert ist, das ist hier undefiniert! Da // SIGUSR1 in der vorherigen (old) threadspezifischen Signalmaske nicht explizit // erlaubt oder blockiert wurde, gilt ab hier wieder die Signalmaske, die uns // unser Elternprozess (unsere Shell, aus der wir das Programm ausgeführt // haben) übergeben hat. sigprocmask(SIG_SETMASK, &old, NULL); printf("Fertig \\o/\n"); }