2015-06-28 15 views
6

Я использую этот учебник с сайта http://www.code2learn.com/2011/01/signal-program-using-parent-child.html и пытаюсь понять, почему сигнал не получен ребенком?Передача сигнала от родителя к ребенку

вот код:

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

выход:

enter image description here

+0

Любые идеи парней ???? – user3162878

ответ

8

Это состояние гонки. Ваш код предполагает, что ребенок запускается первым и не выгружается родителем до тех пор, пока он не установит все обработчики сигналов и не начнет цикл навсегда.

Если это не так, родитель может отправить сигнал ребенку до того, как ребенок успеет поймать сигнал. Таким образом, дочерний процесс убивается, поскольку действие по умолчанию для SIGHUP, SIGINT и SIGQUIT должно завершиться.

В вашем конкретном случае вы никогда не увидите выход ребенка. Это означает, что родитель отправил SIGHUP ребенку, а SIGHUP был доставлен до того, как ребенок изменил поведение по умолчанию. Таким образом, ребенок был убит.

На самом деле, если вы сделали ошибку проверки на возвращающее значении kill(2) - что вы должны - вы бы увидели ESRCH в родительском при попытке отправить SIGINT и SIGQUIT, потому что ребенок уже пошел (не предполагая никакого другого процесса в система была запущена и получила тем же PID в то же время).

Итак, как вы это исправите? Либо используйте некоторую форму синхронизации, чтобы заставить ребенка запускаться первым, и разрешить родителям выполнять только после того, как все обработчики сигналов установлены, или настроить обработчики сигналов до, а затем отключить их в родительском. Приведенный ниже код использует последний подход:

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

Кроме того, вы не должны использовать signal(2): она ненадежна во многих отношениях, и его точная семантика зависит от платформы. Чтобы обеспечить максимальную мобильность, вы должны использовать sigaction(2). Обратитесь к справочникам, чтобы узнать больше. Вот тот же самый код, используя вместо sigaction(2):

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

Последнее, но не менее, важно отметить, что вы всегда должны компилировать с -Wall. Ваша программа имеет некоторые ошибки:

  • Возвращаемый тип main() должен быть int.
  • Обработчики сигналов получают номер сигнала в качестве аргумента, пожалуйста, используйте правильный прототип и декларацию.
  • fork(2) возвращает pid_t, а не int, пожалуйста, используйте правильный тип.
  • Чтобы получить правильный прототип для fork(2), вам необходимо указать unistd.h.
  • printf(3) не является безопасным для асинхронного сигнала, и поэтому вы не должны вызывать его внутри обработчика сигнала. В этой игрушечной программе все в порядке, чтобы увидеть, как сигналы работают вместе, но имейте в виду, что вы никогда не должны делать это в реальном мире.Чтобы просмотреть список безопасных функций асинхронного сигнала, а также действия по умолчанию для каждого сигнала, см. man 7 signal.

Совет: прекратите обучение с этого сайта. Если вы хотите изучить этот материал, прочитайте Расширенное программирование в среде UNIX. Идите прямо к chaper 10, чтобы узнать, почему именно signal(2) считается ненадежным и устаревшим. Это большая книга, но стоит потратить на нее свое время.

+0

Спасибо, брат действительно помог. И я бы дал дополнительный голос за предложение хорошей книги, если бы это было возможно. – user3162878

+0

Вторичные книги вместо веб-сайтов (и другие убедительные, подробные объяснения). – msw

Смежные вопросы