2015-06-28 13 views
6

Sto utilizzando questo tutorial dal sito Web http://www.code2learn.com/2011/01/signal-program-using-parent-child.html e sto cercando di capire perché il segnale non viene ricevuto dal bambino?invio segnale da genitore a figlio

ecco il codice:

#include <stdio.h> 
#include <signal.h> 
#include <stdlib.h> 
void sighup(); /* routines child will call upon sigtrap */ 
void sigint(); 
void sigquit(); 
void main() 
{ int pid; 
    /* get child process */ 
    if ((pid = fork()) < 0) { 
     perror("fork"); 
     exit(1); 
    } 
    if (pid == 0) 
    { /* child */ 
     signal(SIGHUP,sighup); /* set function calls */ 
     signal(SIGINT,sigint); 
     signal(SIGQUIT, sigquit); 
     for(;;); /* loop for ever */ 
    } 
    else /* parent */ 
    { /* pid hold id of child */ 
     printf("\nPARENT: sending SIGHUP\n\n"); 
     kill(pid,SIGHUP); 
     sleep(3); /* pause for 3 secs */ 
     printf("\nPARENT: sending SIGINT\n\n"); 
     kill(pid,SIGINT); 
     sleep(3); /* pause for 3 secs */ 
     printf("\nPARENT: sending SIGQUIT\n\n"); 
     kill(pid,SIGQUIT); 
     sleep(3); 
    } 
} 
void sighup() 
{ signal(SIGHUP,sighup); /* reset signal */ 
    printf("CHILD: I have received a SIGHUP\n"); 
} 
void sigint() 
{ signal(SIGINT,sigint); /* reset signal */ 
    printf("CHILD: I have received a SIGINT\n"); 
} 
void sigquit() 
{ printf("My DADDY has Killed me!!!\n"); 
    exit(0); 
} 

uscita:

enter image description here

+0

Qualche idea ragazzi ???? – user3162878

risposta

8

E 'una condizione di competizione. Il tuo codice presuppone che il bambino sia eseguito per primo e non venga preventivato dal genitore fino a quando non ha installato tutti i gestori di segnale e inizia il ciclo per sempre.

In caso contrario, il genitore può inviare un segnale al bambino prima che il bambino abbia la possibilità di rilevare il segnale. Come tale, il processo figlio viene ucciso, poiché l'azione predefinita per SIGHUP, SIGINT e SIGQUIT deve terminare.

Nel tuo caso specifico, non vedi mai alcun output dal bambino. Ciò significa che il genitore ha inviato SIGHUP al minore e SIGHUP è stato consegnato prima che il bambino abbia modificato il comportamento predefinito. Quindi il bambino è stato ucciso.

In realtà, se hai fatto qualche errore di controllo sul valore di ritorno del kill(2) - che si dovrebbe - si vedrebbe ESRCH nel genitore su tentativo di inviare SIGINT e SIGQUIT, perché il bambino è già andato (supponendo che nessun altro processo in il sistema è stato avviato e nel frattempo è stato assegnato lo stesso PID).

Quindi, come lo riparate? O utilizzare una qualche forma di sincronizzazione per forzare il figlio a eseguire prima e lasciare che il genitore esegua dopo che tutti i gestori di segnale sono installati, oppure impostare i gestori di segnale prima del biforcazione e quindi disinserirli nel genitore. Il seguente codice utilizza il secondo approccio:

#include <stdio.h> 
#include <signal.h> 
#include <stdlib.h> 
#include <unistd.h> 

void sighup(int); /* routines child will call upon sigtrap */ 
void sigint(int); 
void sigquit(int); 

int main(void) { 

    int pid; 

    signal(SIGHUP,sighup); /* set function calls */ 
    signal(SIGINT,sigint); 
    signal(SIGQUIT, sigquit); 

    /* get child process */ 
    if ((pid = fork()) < 0) { 
     perror("fork"); 
     exit(1); 
    } 

    if (pid == 0) { 

     /* child */ 
     for(;;); /* loop for ever */ 

    } else { 

     signal(SIGHUP, SIG_DFL); 
     signal(SIGINT, SIG_DFL); 
     signal(SIGQUIT, SIG_DFL); 

     /* parent */ 
     /* pid hold id of child */ 
     printf("\nPARENT: sending SIGHUP\n\n"); 
     kill(pid,SIGHUP); 
     sleep(3); /* pause for 3 secs */ 
     printf("\nPARENT: sending SIGINT\n\n"); 
     kill(pid,SIGINT); 
     sleep(3); /* pause for 3 secs */ 
     printf("\nPARENT: sending SIGQUIT\n\n"); 
     kill(pid,SIGQUIT); 
     sleep(3); 
    } 

    return 0; 
} 

void sighup(int signo) { 
    signal(SIGHUP,sighup); /* reset signal */ 
    printf("CHILD: I have received a SIGHUP\n"); 
} 

void sigint(int signo) { 
    signal(SIGINT,sigint); /* reset signal */ 
    printf("CHILD: I have received a SIGINT\n"); 
} 

void sigquit(int signo) { 
    printf("My DADDY has Killed me!!!\n"); 
    exit(0); 
} 

Inoltre, non dovrebbe usare signal(2): è inaffidabile in molti modi, e la sua semantica esatte sono dipendente dalla piattaforma. Per garantire la massima portabilità, è necessario utilizzare sigaction(2). Fare riferimento alle manpage per saperne di più. Ecco lo stesso codice utilizzando sigaction(2) invece:

#include <stdio.h> 
#include <signal.h> 
#include <stdlib.h> 
#include <unistd.h> 

void sighup(int); /* routines child will call upon sigtrap */ 
void sigint(int); 
void sigquit(int); 

int main(void) { 

    struct sigaction sigact; 
    sigact.sa_flags = 0; 
    sigemptyset(&sigact.sa_mask); 

    sigact.sa_handler = sighup; 
    if (sigaction(SIGHUP, &sigact, NULL) < 0) { 
     perror("sigaction()"); 
     exit(1); 
    } 

    sigact.sa_handler = sigint; 
    if (sigaction(SIGINT, &sigact, NULL) < 0) { 
     perror("sigaction()"); 
     exit(1); 
    } 

    sigact.sa_handler = sigquit; 
    if (sigaction(SIGQUIT, &sigact, NULL) < 0) { 
     perror("sigaction()"); 
     exit(1); 
    } 

    pid_t pid; 
    /* get child process */ 
    if ((pid = fork()) < 0) { 
     perror("fork"); 
     exit(1); 
    } 

    if (pid == 0) { 

     /* child */ 
     for(;;); /* loop for ever */ 

    } else { 

     sigact.sa_handler = SIG_DFL; 
     sigaction(SIGHUP, &sigact, NULL); 
     sigaction(SIGINT, &sigact, NULL); 
     sigaction(SIGQUIT, &sigact, NULL); 

     /* parent */ 
     /* pid hold id of child */ 
     printf("\nPARENT: sending SIGHUP\n\n"); 
     kill(pid,SIGHUP); 
     sleep(3); /* pause for 3 secs */ 
     printf("\nPARENT: sending SIGINT\n\n"); 
     kill(pid,SIGINT); 
     sleep(3); /* pause for 3 secs */ 
     printf("\nPARENT: sending SIGQUIT\n\n"); 
     kill(pid,SIGQUIT); 
     sleep(3); 
    } 

    return 0; 
} 

void sighup(int signo) { 
    signal(SIGHUP,sighup); /* reset signal */ 
    printf("CHILD: I have received a SIGHUP\n"); 
} 

void sigint(int signo) { 
    signal(SIGINT,sigint); /* reset signal */ 
    printf("CHILD: I have received a SIGINT\n"); 
} 

void sigquit(int signo) { 
    printf("My DADDY has Killed me!!!\n"); 
    exit(0); 
} 

Ultimo, ma non meno importante, è importante ricordare che si dovrebbe sempre compilare con -Wall. Il vostro programma ha alcuni errori:

  • Il tipo di ritorno di main() dovrebbe essere int.
  • I gestori di segnale ricevono il numero del segnale come argomento, si prega di utilizzare il prototipo e la dichiarazione corretti.
  • fork(2) restituisce un pid_t, non uno int, utilizzare il tipo corretto.
  • È necessario includere unistd.h per ottenere il prototipo corretto per fork(2).
  • printf(3) non è sicuro per il segnale asincrono e pertanto non è necessario chiamarlo all'interno di un gestore di segnale. Va bene in questo programma di giocattoli per vedere come i segnali funzionano insieme, ma tieni presente che non dovresti mai farlo nel mondo reale.Per visualizzare un elenco di funzioni sicure per segnale asincrono, nonché le azioni predefinite per ciascun segnale, vedere man 7 signal.

Un consiglio: smettere di imparare da quel sito. Se vuoi imparare questo tipo di cose, leggi Programmazione avanzata nell'ambiente UNIX. Vai direttamente al chaper 10 per sapere perché esattamente signal(2) è considerato inaffidabile e obsoleto. È un grande libro, ma vale la pena investire il tuo tempo su di esso.

+0

Grazie fratello davvero aiutato. E darei un voto extra per suggerire un buon libro se fosse possibile. – user3162878

+0

Libri secondari anziché siti Web (e altre spiegazioni dettagliate e dettagliate). – msw