Ich glaube, Sie haben eines der klassischen Probleme mit Signalen.
while(1){
if(receivedP==0){
printf("parent suspend\n");
sigsuspend(&setParent);
}
receivedP=0;
printf("parent %d\n",c);
kill(pf,SIGUSR1);
}
Stellen Sie sich vor, was passiert, wenn das Signal von dem Kind zwischen den Anweisungen für if(receivedP==0)
und sigsuspend(&setParent)
ankommt. Der Handler wird ausgeführt und setzt receivedP
auf eins, aber die Hauptschleife prüft sie nicht erneut; es wird in sigsuspend
gehen und nie herauskommen.
Um sigsuspend
sicher zu nutzen, benötigen Sie, um die Signale zu haben, kümmert sich um jederzeit gesperrt werden, wenn das Programm nichtsigsuspend
Aufruf ist. Sie tun das mit sigprocmask
. Es ist auch notwendig, um sicherzustellen, dass die Signale während der Ausführung des Handlers, blockiert werden, die Sie sigaction
statt signal
zu verwenden, erfordert (aber Sie sollten die sowieso tun, als signal
stark underspecified und System-zu-System-Variationen wird beißen Sie in den Arsch).
Sobald Sie sicher, dass das Signal nur während eines sigsuspend
geliefert werden, müssen Sie nicht mehr die receivedP
und receivedC
Variablen; Sie wissen, dass das Signal passiert ist, oder sigsuspend
wäre nicht zurückgekehrt. (Dies wäre nicht wahr, wenn Ihr Programm in jedem Prozess auf mehr als ein einzelnes Signal gewartet hätte, aber an diesem Punkt wird es viel komplizierter; machen Sie sich keine Sorgen, bis es auftaucht.)
Tatsächlich Sobald Sie sicherstellen, dass Sie keine irgendetwas im Signalhandler tun müssen. Ihre Zählervariable kann lokal parent
und child
sein. Es ist immer am besten, so wenig wie möglich in einem Signal-Handler zu tun; Der Buchstabe des C-Standards ermöglicht es Ihnen, fast nichts zu tun, ohne unbestimmtes Verhalten zu riskieren, und POSIX öffnet es nur ein wenig mehr. (Übung für Sie: Ändern Sie dieses Programm so, dass es sigwaitinfo
verwendet, so dass es keine Handler-Funktionen benötigt.)
Diese Änderung Ihres Programms funktioniert zuverlässig für mich. Ich korrigierte auch eine Reihe anderer Art Probleme und kleinerer Fehler: beachten Sie die Schlaufen in parent
und child
Dinge zu tun, in unterschiedlicher Reihenfolge, die Fehler in main
prüfen, und ich bin nur blockiert SIGUSR1
und SIGUSR2
, weil es mehr andere Signale das sollte erlaubt sein, den Prozess zu beenden (SIGTERM
, SIGHUP
, SIGQUIT
, SIGSEGV
, ...) und Sie möchten nicht eine Liste pflegen müssen. Es genügt, die Signale zu blockieren, für die das Programm Handler installiert hat.
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
static void handler(int unused)
{
}
static void child(sigset_t *ss)
{
unsigned int c = 0;
pid_t parent_pid = getppid();
sigdelset(ss, SIGUSR1);
for (;;) {
sigsuspend(ss);
printf("child %u\n", c++);
kill(parent_pid, SIGUSR2);
}
}
static void parent(sigset_t *ss, pid_t child_pid)
{
unsigned int c = 0;
sigdelset(ss, SIGUSR2);
for (;;) {
printf("parent %u\n", c++);
kill(child_pid, SIGUSR1);
sigsuspend(ss);
}
}
int main(void)
{
// Ensure line-buffered stdout.
if (setvbuf(stdout, 0, _IOLBF, 0)) {
perror("setvbuf");
return 1;
}
// This signal mask is in effect at all times _except_ when sleeping
// in sigsuspend(). Note that _only_ the signals used for IPC are
// blocked. After forking, each process will modify it appropriately
// for its own use of sigsuspend(); this does not affect the kernel-side
// copy made by sigprocmask().
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss, SIGUSR1);
sigaddset(&ss, SIGUSR2);
if (sigprocmask(SIG_BLOCK, &ss, 0)) {
perror("sigprocmask");
return 1;
}
// Always use sigaction(), not signal(); signal() is underspecified.
// The mask here is the signal mask to use _while the handler is
// executing_; it should also block both IPC signals.
struct sigaction sa;
sa.sa_handler = handler;
sa.sa_mask = ss;
sa.sa_flags = SA_RESTART;
if (sigaction(SIGUSR1, &sa, 0) || sigaction(SIGUSR2, &sa, 0)) {
perror("sigaction");
return 1;
}
pid_t child_pid = fork();
if (child_pid < 0) {
perror("fork");
return 1;
}
if (child_pid == 0)
child(&ss);
else
parent(&ss, child_pid);
// we never get here but the compiler might not know that
return 0;
}
Ich empfehle Ihnen, die GNU C Library Handbuch lesen section on signal handling den ganzen Weg durch; Es enthält einige weitere nützliche Hinweise zur sicheren Verwendung von Signalen.
1) Sie dürfen async-unsafe Funktionen wie 'printf()' nicht in Signalhandlern aufrufen. 2) Sie dürfen nicht auf nicht blockierungsfreie'_Atomische', nicht-flüchtige sig_atomische_t'-Objekte sowohl von normalem Code als auch innerhalb eines Signal-Handlers zugreifen. Ansonsten undefiniertes Verhalten. – EOF
@EOF ok, also habe ich 'printf()' aus 'handler (int s)' entfernt. Ich verstehe nicht '2)' – Elirovi
@EOF Was Sie sagen, ist im Allgemeinen wahr, aber der ganze Sinn von 'sigsuspend' ist, dass Sie asynchrone Signale nur an einem sicheren Punkt geliefert bekommen können - dh wenn der Hauptteil von Die Ausführung hat gerade "sigsuspend" genannt. Wenn es richtig gemacht wird, ist es in Ordnung, in den Signalhandlern willkürlich zu arbeiten. (Dieses Programm scheint es nicht richtig zu machen.) – zwol