Это состояние гонки. Ваш код предполагает, что ребенок запускается первым и не выгружается родителем до тех пор, пока он не установит все обработчики сигналов и не начнет цикл навсегда.
Если это не так, родитель может отправить сигнал ребенку до того, как ребенок успеет поймать сигнал. Таким образом, дочерний процесс убивается, поскольку действие по умолчанию для 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)
считается ненадежным и устаревшим. Это большая книга, но стоит потратить на нее свое время.
Любые идеи парней ???? – user3162878