我正在编写一个Unix程序,其中父进程分叉一个子进程,之后它必须发送一个由handler_sigusr1处理的SIGUSR1信号和一个SIGKILL信号来完成该程序。 我的问题是,当我运行程序时,子程序应该接收SIGUSR1然后接收SIGKILL,但它只接收SIGKILL。 我的代码有问题吗?谢谢你的帮助!
void handler_sigusr1()
{
printf("Signal SIGUSR1\n");
}
int main(int argc, char **argv)
{
struct sigaction action;
sigset_t new_mask, oldmask;
int status;
action.sa_flags = 0;
sigaction(SIGTERM, &action, NULL);
sigemptyset(&new_mask);
sigaddset(&new_mask, SIGTERM);
sigaction(SIGUSR1, &action, NULL);
action.sa_handler = handler_sigusr1;
sigaddset(&new_mask, SIGUSR1);
child1 = fork();
if(child1 == 0)
{
sigprocmask(SIG_BLOCK, &new_mask, &oldmask);
sigsuspend(&new_mask);
}
if(child1 != 0)
{
kill(child1, SIGUSR1);
kill(child1, SIGKILL);
}
wait(&status);
}
答案 0 :(得分:7)
有4个问题:
设置信号处理程序的代码是错误的。
这两行的顺序错误:
sigaction(SIGUSR1, &action, NULL);
action.sa_handler = handler_sigusr1;
他们应该
action.sa_handler = handler_sigusr1;
sigaction(SIGUSR1, &action, NULL);
您的父进程在发送SIGUSR1后立即将SIGKILL发送给您的孩子。 SIGKILL将终止您的子进程 - 不能阻止或忽略它。
除非你很幸运,否则你的孩子的sigusr1处理程序将无法在父发送SIGKILL之前运行或完成 - 这会立即终止子进程。
您可以通过插入类似的延迟来大大增加SIGUSR1的交付和处理机会:
kill(child1, SIGUSR1);
sleep(1);
kill(child1, SIGKILL);
您已阻止SIGUSR1,因此无法将其传送到您的流程。
删除行
sigaddset(&new_mask, SIGUSR1);
请注意,当使用sigprocmask()
或sigsuspend()
设置信号掩码时,掩码中设置的信号是阻止的信号。
您无法在信号处理程序中安全地使用printf()。
printf()不是信号异步安全,也不能在信号处理程序中使用。如果您不幸,它可能不会像在信号处理程序中使用时那样表现。使用write()代替直接写入stdout,例如
write(1, "Signal SIGUSR1\n", 15);
答案 1 :(得分:4)
这里是您的代码的修改版本,似乎有效。请注意,没有进程可以阻止SIGKILL或SIGSTOP。我删除了SIGTERM代码,因为没有使用。信号处理程序调用{{1}}而不是write()
,因为printf()
是异步信号安全的。它将信号编号手动编码到消息中,因为write()
等函数不是异步信号安全的(正式都不是sprintf()
) - 有关详细信息,请参阅How to avoid using printf()
in a signal handler。
strlen()
示例运行:
#include <signal.h>
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
static void handler_sigusr1(int signum)
{
char msg[] = "Signal ?? SIGUSR1\n";
msg[7] = (signum / 10) + '0';
msg[8] = (signum % 10) + '0';
write(2, msg, sizeof(msg) - 1);
}
int main(void)
{
struct sigaction action;
action.sa_flags = 0;
action.sa_handler = handler_sigusr1;
sigemptyset(&action.sa_mask);
sigaction(SIGUSR1, &action, NULL);
int child1 = fork();
if (child1 == 0)
{
sigset_t new_mask, oldmask;
sigemptyset(&new_mask);
sigaddset(&new_mask, SIGUSR1);
sigprocmask(SIG_BLOCK, &new_mask, &oldmask);
sigsuspend(&new_mask);
printf("Child (%d) unsuspended\n", (int)getpid());
}
if (child1 != 0)
{
kill(child1, SIGUSR1);
sleep(1);
kill(child1, SIGKILL);
int status;
int corpse = wait(&status);
printf("Child (%d) - corpse = %d (0x%.4X)\n", child1, corpse, status);
}
return 0;
}
显然,第一条消息来自孩子的信号处理程序。它表明已收到SIGUSR1。第二条消息来自父母。十六进制数表示孩子死于信号9(又名SIGKILL)。
子进程中的代码是做什么的?答:困惑我(也可能是你)。我在Signal 30 SIGUSR1
Child (41875) - corpse = 41875 (0x0009)
之前添加了额外的printf()
,然后运行代码并显示:
sigsuspend()
Signal 30 SIGUSR1
Child (42688) suspending
Child (42688) - corpse = 42688 (0x0009)
调用会阻止SIGUSR1,但我的计算机上的父级太快,在生效之前获取信号。 sigprocmask()
然后在SIGUSR1被阻止的情况下进入睡眠状态;由于SIGKILL,这个过程就会消失。
此代码变体根本不使用sigsuspend()
。它有一个非常不同的效果:
sigprocmask()
示例输出:
#include <signal.h>
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
static void signal_handler(int signum)
{
char msg[] = "Signal ??\n";
msg[7] = (signum / 10) + '0';
msg[8] = (signum % 10) + '0';
write(2, msg, sizeof(msg) - 1);
}
int main(void)
{
struct sigaction action;
action.sa_flags = 0;
action.sa_handler = signal_handler;
sigemptyset(&action.sa_mask);
sigaction(SIGUSR1, &action, NULL);
sigaction(SIGTERM, &action, NULL);
int child1 = fork();
if (child1 == 0)
{
sigset_t new_mask;
sigemptyset(&new_mask);
printf("Child (%d) suspending\n", (int)getpid());
sigsuspend(&new_mask);
printf("Child (%d) unsuspended\n", (int)getpid());
}
else
{
kill(child1, SIGUSR1);
sleep(1);
kill(child1, SIGTERM);
sleep(1);
kill(child1, SIGUSR1);
sleep(1);
kill(child1, SIGKILL);
int status;
int corpse = wait(&status);
printf("Child (%d) - corpse = %d (0x%.4X)\n", child1, corpse, status);
}
return 0;
}
这次,孩子在报告已取消暂停之后正常死亡,因为Signal 30
Child (42969) suspending
Signal 15
Child (42969) unsuspended
Child (42969) - corpse = 42969 (0x0000)
和SIGTERM
未被阻止。
您可以响应变体,将SIGUSR1
和/或SIGTERM
添加到传递给SIGUSR1
的信号掩码中,可能是为了响应不同的命令行选项。
我注意到macOS Sierra上sigsuspend()
的手册页说:
信号掩码设置通常为空,表示在呼叫期间所有信号都将被解锁。
答案 2 :(得分:0)
简单 - 子进程在被杀死之前没有时间处理SIGUSR1信号。