从 parent 向 child 发送信号
sending signal from parent to child
我正在使用来自网站 http://www.code2learn.com/2011/01/signal-program-using-parent-child.html 的本教程,并试图了解为什么 child 没有收到信号?
代码如下:
#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);
}
输出:
这是一个竞争条件。您的代码假定 child 运行 是第一个并且不会被 parent 抢占,直到它安装了所有信号处理程序并开始永远循环。
如果不是这种情况,parent 可能会在 child 有机会捕捉到信号之前向 child 发送信号。因此,child 进程被终止,因为 SIGHUP
、SIGINT
和 SIGQUIT
的默认操作是终止。
在您的特定情况下,您永远不会看到 child 的任何输出。这意味着 parent 将 SIGHUP
发送到 child,并且 SIGHUP
在 child 更改默认行为之前交付。所以child被杀死了。
实际上,如果您对 kill(2)
的 returning 值做了一些错误检查 - 您应该这样做 - 您会在尝试时在 parent 中看到 ESRCH
发送 SIGINT
和 SIGQUIT
,因为 child 已经消失(假设系统中没有其他进程启动并且同时分配了相同的 PID)。
那么,你是如何解决的?要么使用某种形式的同步来强制 child 先到 运行 并且只让 parent 在所有信号处理程序安装之后执行,或者在 之前设置信号处理程序 分叉,然后在 parent 中取消设置。下面的代码使用后一种方法:
#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()
的return类型应该是int
。
- 信号处理程序接收信号编号作为参数,请使用正确的原型和声明。
fork(2)
return是pid_t
,不是int
,请使用正确的类型。
- 您需要包含
unistd.h
以获得 fork(2)
的正确原型。
printf(3)
不 async-signal 安全,因此您不应该在信号处理程序中调用它。可以在这个玩具程序中查看信号如何协同工作,但请记住,您永远不应该在现实世界中这样做。要查看 async-signal 安全功能列表以及每个信号的默认操作,请参阅 man 7 signal
。
忠告:停止从该网站学习。如果你想学习这类东西,请阅读 UNIX 环境中的高级编程。直接转到第 10 章,了解为什么 signal(2)
被认为是不可靠和过时的。这是一本厚书,但值得花时间阅读。
我正在使用来自网站 http://www.code2learn.com/2011/01/signal-program-using-parent-child.html 的本教程,并试图了解为什么 child 没有收到信号?
代码如下:
#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);
}
输出:
这是一个竞争条件。您的代码假定 child 运行 是第一个并且不会被 parent 抢占,直到它安装了所有信号处理程序并开始永远循环。
如果不是这种情况,parent 可能会在 child 有机会捕捉到信号之前向 child 发送信号。因此,child 进程被终止,因为 SIGHUP
、SIGINT
和 SIGQUIT
的默认操作是终止。
在您的特定情况下,您永远不会看到 child 的任何输出。这意味着 parent 将 SIGHUP
发送到 child,并且 SIGHUP
在 child 更改默认行为之前交付。所以child被杀死了。
实际上,如果您对 kill(2)
的 returning 值做了一些错误检查 - 您应该这样做 - 您会在尝试时在 parent 中看到 ESRCH
发送 SIGINT
和 SIGQUIT
,因为 child 已经消失(假设系统中没有其他进程启动并且同时分配了相同的 PID)。
那么,你是如何解决的?要么使用某种形式的同步来强制 child 先到 运行 并且只让 parent 在所有信号处理程序安装之后执行,或者在 之前设置信号处理程序 分叉,然后在 parent 中取消设置。下面的代码使用后一种方法:
#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()
的return类型应该是int
。- 信号处理程序接收信号编号作为参数,请使用正确的原型和声明。
fork(2)
return是pid_t
,不是int
,请使用正确的类型。- 您需要包含
unistd.h
以获得fork(2)
的正确原型。 printf(3)
不 async-signal 安全,因此您不应该在信号处理程序中调用它。可以在这个玩具程序中查看信号如何协同工作,但请记住,您永远不应该在现实世界中这样做。要查看 async-signal 安全功能列表以及每个信号的默认操作,请参阅man 7 signal
。
忠告:停止从该网站学习。如果你想学习这类东西,请阅读 UNIX 环境中的高级编程。直接转到第 10 章,了解为什么 signal(2)
被认为是不可靠和过时的。这是一本厚书,但值得花时间阅读。