Beispiellösung

Content

Antworten

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
25974 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' [-Wimplicit-function-declaration]
       sighold(SIGTERM); sighold(SIGUSR1);
       ^~~~~~~
tokenring2.c:28:7: warning: implicit declaration of function 'sigrelse' [-Wimplicit-function-declaration]
       sigrelse(SIGTERM); sighold(SIGUSR1);
       ^~~~~~~~
heim$ gcc -Wall -D_XOPEN_SOURCE=600 -o tokenring2 tokenring2.c
heim$ ./tokenring2
48520 rounds
heim$