2016-11-01 2 views
1

ich ein Kind gegabelt und ich versuche, sie zu synchronisieren, so dass sieKeine Spur des Kindes Prozess

child 0 
parent 0 
child 1 
parent 1 

drucken Ich habe zwar sigsuspend verwenden, ist dies mein Code für den Moment und alles, was ich bekommen ist parent suspend . Es gibt keine Spur von dem Kind.

int c=0, receivedP=0, receivedC=0; 
sigset_t setParent, setChild; 
void handler(int s){ 
    if(s==SIGUSR1){ 
     receivedC=1; 
     printf("parent --sig1--> child\n"); 
     c++; 
    } 
    else{ 
     receivedP=1; 
     printf("child --sig2--> parent\n"); 
    } 
} 
void child(){ 
    sigfillset(&setChild); 
    sigdelset(&setChild,SIGUSR1); 
    sigdelset(&setChild,SIGINT); //this makes me able to terminate the program at any time 
    while(1){ 
     if(receivedC==0){ 
      printf("child suspend\n"); 
      sigsuspend(&setChild); 
     } 
     receivedC=0; 
     printf("child %d\n",c); 
     kill(getppid(),SIGUSR2); 
    } 
} 
void parent(pid_t pf){ 
    sigfillset(&setParent); 
    sigdelset(&setParent,SIGUSR2); 
    sigdelset(&setParent,SIGINT); //this makes me able to terminate the program at any time 
    kill(pf,SIGUSR1); 
    while(1){ 
     if(receivedP==0){ 
      printf("parent suspend\n"); 
      sigsuspend(&setParent); 
     } 
     receivedP=0; 
     printf("parent %d\n",c); 
     kill(pf,SIGUSR1); 
    } 
} 
int main(){ 
    signal(SIGUSR1,handler); 
    signal(SIGUSR2,handler); 
    pid_t p; 
    p= fork(); 
    if(!p)child(); 
    else parent(p); 
    return 0; 
} 

Weiß jemand, was das verursacht?

+1

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

+0

@EOF ok, also habe ich 'printf()' aus 'handler (int s)' entfernt. Ich verstehe nicht '2)' – Elirovi

+0

@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

Antwort

1

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.

Verwandte Themen