Linux 信号:Child 未收到
Linux Signal : Child not receiving
我的代码是一个简单的 C 代码,其中有两个进程,第一个是 parents 发送信号并打印出它发送的内容,另一个是 child 接收信号并打印出它发送的内容已收到
我的代码的输出是
PARENT: 发送 SIGHUP
PARENT: 发送 SIGINT
PARENT: 发送 SIGQUIT
它应该是
PARENT: 发送 SIGHUP
child 收到 SIGHUP
等...
// C program to implement sighup(), sigint()
// and sigquit() signal functions
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
// function declaration
void sighup(int);
void sigint(int);
void sigquit(int );
void sigsegv(int );
// driver code
int main()
{
int pid;
/* get child process */
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0) { /* child */
signal(SIGHUP, sighup);
signal(SIGINT, sigint);
signal(SIGQUIT, sigquit);
signal(SIGSEGV, sigsegv);
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);
}
return 0 ;
}
// sighup() function definition
void sighup(int signo)
{
signal(SIGHUP, sighup); /* reset signal */
printf("CHILD: 1 [sighub]\n");
}
// sigint() function definition
void sigint(int signo)
{
signal(SIGINT, sigint); /* reset signal */
printf("CHILD: 2 [sigint]\n");
}
// sigsegv() function definition
void sigsegv(int signo)
{
signal(SIGSEGV, sigsegv); /* reset signal */
printf("CHILD: 11 [sigsegv]\n");
}
// sigquit() function definition
void sigquit(int signo)
{
signal(SIGINT, sigquit); /* reset signal */
printf("3 [sigquit]\n");
}
可能第一个信号是在 child 初始化它的信号处理之前发送的。至少我可以通过在 if (pid == 0) {
.
之后直接添加 sleep(2);
来重现该行为
尝试在开始发送信号之前在 parent 进程中添加一个 sleep()
您的代码逻辑有几个“问题”(除了使用非 async-signal-safe 函数)。
无法保证哪个进程将在 fork()
之后开始工作。这取决于内核和系统。 Linux 内核允许您通过在 /proc/sys/kernel/sched_child_runs_first
中设置一个值来使其成为确定性的,其中如果值为 0(默认)则 parent 运行 首先,如果值为non-zero child 运行 首先。尽管如此,依赖此开关进行同步并不是一个好的做法,因为在多处理器环境中,如果我们有可用的 CPU,两者可以同时运行。
进程可以在任何给定时刻 pre-empted,这可能是由于使用了系统调用,也可能是由于 cpu 时间。因此,无法保证 child 在 parent 发出信号之前设置了所有信号处理程序,因此一些信号丢失了(实际上在我的测试中,我得到了所有信号都可用的情况丢失,一旦睡眠被移除)。
和2一样的原因,不能保证child在parent发送新信号之前收到信号。
为了解决问题,保证执行顺序,需要创建同步屏障。这是基于确保同步的 IPC 管道的示例。在示例中,parent 等待 child 到 set-up 信号,然后,对于它发送的每个信号,它等待 child 确认信号已收到。
// C program to implement sighup(), sigint()
// and sigquit() signal functions
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
// function declaration
void sighup(int);
void sigint(int);
void sigquit(int );
void sigsegv(int );
int fd[2]; // communication pipe
// driver code
int main()
{
int pid;
if (pipe(fd)==-1)
{
fprintf(stderr, "Pipe Failed" );
exit(1);
}
/* get child process */
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0) { /* child */
close(fd[0]); // Close reading end
printf("Preparing the signals");
signal(SIGHUP, sighup);
printf(".");
signal(SIGINT, sigint);
printf(".");
signal(SIGQUIT, sigquit);
printf(".");
signal(SIGSEGV, sigsegv);
printf(".Signals ready\n");
write(fd[1],"ready[=10=]", 6);
for (;;)
; /* loop for ever */
}
else /* parent */
{ /* pid hold id of child */
char readb[1024];
readb[1023] = 0; //Safty stop
close(fd[1]); // Close writting end
printf("Waiting for child to finish\n");
read(fd[0], readb, 1023);
printf("Child sends %s", readb);
printf("\nPARENT: sending SIGHUP\n\n");
kill(pid, SIGHUP);
read(fd[0], readb, 1023);
printf("%s\n", readb);
printf("\nPARENT: sending SIGINT\n\n");
kill(pid, SIGINT);
read(fd[0], readb, 1023);
printf("%s\n", readb);
printf("\nPARENT: sending SIGQUIT\n\n");
kill(pid, SIGQUIT);
read(fd[0], readb, 1023);
printf("%s\n", readb);
close(fd[0]);
}
return 0 ;
}
// sighup() function definition
void sighup(int signo)
{
signal(SIGHUP, sighup); /* reset signal */
write(fd[1],"CHILD: 1 [sighub][=10=]",18);
}
// sigint() function definition
void sigint(int signo)
{
signal(SIGINT, sigint); /* reset signal */
write(fd[1],"CHILD: 2 [sigint][=10=]",18);
}
// sigsegv() function definition
void sigsegv(int signo)
{
signal(SIGSEGV, sigsegv); /* reset signal */
write(fd[1],"CHILD: 11 [sigsegv][=10=]",20);
}
// sigquit() function definition
void sigquit(int signo)
{
signal(SIGINT, sigquit); /* reset signal */
write(fd[1],"CHILD: 3 [sigquit][=10=]",19);
}
我的代码是一个简单的 C 代码,其中有两个进程,第一个是 parents 发送信号并打印出它发送的内容,另一个是 child 接收信号并打印出它发送的内容已收到 我的代码的输出是 PARENT: 发送 SIGHUP PARENT: 发送 SIGINT PARENT: 发送 SIGQUIT 它应该是 PARENT: 发送 SIGHUP child 收到 SIGHUP 等...
// C program to implement sighup(), sigint()
// and sigquit() signal functions
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
// function declaration
void sighup(int);
void sigint(int);
void sigquit(int );
void sigsegv(int );
// driver code
int main()
{
int pid;
/* get child process */
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0) { /* child */
signal(SIGHUP, sighup);
signal(SIGINT, sigint);
signal(SIGQUIT, sigquit);
signal(SIGSEGV, sigsegv);
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);
}
return 0 ;
}
// sighup() function definition
void sighup(int signo)
{
signal(SIGHUP, sighup); /* reset signal */
printf("CHILD: 1 [sighub]\n");
}
// sigint() function definition
void sigint(int signo)
{
signal(SIGINT, sigint); /* reset signal */
printf("CHILD: 2 [sigint]\n");
}
// sigsegv() function definition
void sigsegv(int signo)
{
signal(SIGSEGV, sigsegv); /* reset signal */
printf("CHILD: 11 [sigsegv]\n");
}
// sigquit() function definition
void sigquit(int signo)
{
signal(SIGINT, sigquit); /* reset signal */
printf("3 [sigquit]\n");
}
可能第一个信号是在 child 初始化它的信号处理之前发送的。至少我可以通过在 if (pid == 0) {
.
sleep(2);
来重现该行为
尝试在开始发送信号之前在 parent 进程中添加一个 sleep()
您的代码逻辑有几个“问题”(除了使用非 async-signal-safe 函数)。
无法保证哪个进程将在
fork()
之后开始工作。这取决于内核和系统。 Linux 内核允许您通过在/proc/sys/kernel/sched_child_runs_first
中设置一个值来使其成为确定性的,其中如果值为 0(默认)则 parent 运行 首先,如果值为non-zero child 运行 首先。尽管如此,依赖此开关进行同步并不是一个好的做法,因为在多处理器环境中,如果我们有可用的 CPU,两者可以同时运行。进程可以在任何给定时刻 pre-empted,这可能是由于使用了系统调用,也可能是由于 cpu 时间。因此,无法保证 child 在 parent 发出信号之前设置了所有信号处理程序,因此一些信号丢失了(实际上在我的测试中,我得到了所有信号都可用的情况丢失,一旦睡眠被移除)。
和2一样的原因,不能保证child在parent发送新信号之前收到信号。
为了解决问题,保证执行顺序,需要创建同步屏障。这是基于确保同步的 IPC 管道的示例。在示例中,parent 等待 child 到 set-up 信号,然后,对于它发送的每个信号,它等待 child 确认信号已收到。
// C program to implement sighup(), sigint()
// and sigquit() signal functions
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
// function declaration
void sighup(int);
void sigint(int);
void sigquit(int );
void sigsegv(int );
int fd[2]; // communication pipe
// driver code
int main()
{
int pid;
if (pipe(fd)==-1)
{
fprintf(stderr, "Pipe Failed" );
exit(1);
}
/* get child process */
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0) { /* child */
close(fd[0]); // Close reading end
printf("Preparing the signals");
signal(SIGHUP, sighup);
printf(".");
signal(SIGINT, sigint);
printf(".");
signal(SIGQUIT, sigquit);
printf(".");
signal(SIGSEGV, sigsegv);
printf(".Signals ready\n");
write(fd[1],"ready[=10=]", 6);
for (;;)
; /* loop for ever */
}
else /* parent */
{ /* pid hold id of child */
char readb[1024];
readb[1023] = 0; //Safty stop
close(fd[1]); // Close writting end
printf("Waiting for child to finish\n");
read(fd[0], readb, 1023);
printf("Child sends %s", readb);
printf("\nPARENT: sending SIGHUP\n\n");
kill(pid, SIGHUP);
read(fd[0], readb, 1023);
printf("%s\n", readb);
printf("\nPARENT: sending SIGINT\n\n");
kill(pid, SIGINT);
read(fd[0], readb, 1023);
printf("%s\n", readb);
printf("\nPARENT: sending SIGQUIT\n\n");
kill(pid, SIGQUIT);
read(fd[0], readb, 1023);
printf("%s\n", readb);
close(fd[0]);
}
return 0 ;
}
// sighup() function definition
void sighup(int signo)
{
signal(SIGHUP, sighup); /* reset signal */
write(fd[1],"CHILD: 1 [sighub][=10=]",18);
}
// sigint() function definition
void sigint(int signo)
{
signal(SIGINT, sigint); /* reset signal */
write(fd[1],"CHILD: 2 [sigint][=10=]",18);
}
// sigsegv() function definition
void sigsegv(int signo)
{
signal(SIGSEGV, sigsegv); /* reset signal */
write(fd[1],"CHILD: 11 [sigsegv][=10=]",20);
}
// sigquit() function definition
void sigquit(int signo)
{
signal(SIGINT, sigquit); /* reset signal */
write(fd[1],"CHILD: 3 [sigquit][=10=]",19);
}