Milan Stephan
Fotografie & IT

Zurück zur SP2 Übersicht

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");
}