我正在编写一个程序,其中子进程和父进程都可以向子进程发送SIGTERM信号。
信号处理程序是这样的:
void custom_signal_handler(int signum, siginfo_t* info, void* ptr) {
if (signum == SIGTERM) {
printf("1\n");
}
else if (signum == SIGCONT) {
printf("2\n");
}
}
(我已经简化了if
中的打印,以使代码更简单。)
对于SIGCONT
信号 - 只有父母可以使用kill(childPid, SIGCONT)
调用此信号。发生这种情况时,孩子的信号处理程序会按预期打印“2
”。
但是,对于SIGTERM
信号,父母可以通过调用kill(childPid, SIGTERM)
来发送raise(SIGTERM)
和孩子来调用它。问题是“1
”仅在孩子提出SIGTERM
信号时打印,而不是在父母调用它时打印。
我已经让孩子的信号处理程序陷入困境:
// set up signal handler
struct sigaction custom_action;
memset(&custom_action, 0, sizeof(custom_action));
custom_action.sa_sigaction = custom_signal_handler;
custom_action.sa_flags = SA_SIGINFO;
// assign signal handlers
if (0 != sigaction(SIGCONT, &custom_action, NULL)) {
printf("Signal registration failed: %s\n",strerror(errno));
return -1;
}
if (0 != sigaction(SIGTERM, &custom_action, NULL)) {
printf("Signal registration failed: %s\n",strerror(errno));
return -1;
}
有什么想法吗?谢谢!
答案 0 :(得分:1)
在对问题的评论中,OP陈述
当相关孩子处于“加注(SIGSTOP)”时,我从父母发送SIGTERM。我认为因为孩子在SIGSTOP中它不运行信号处理程序。
正确。当流程停止时,它不会收到SIGCONT
和SIGKILL
以外的信号(加SIGSTOP
,SIGTSTP
,SIGTTIN
,SIGTTOU
忽略)。当进程继续时,所有其他信号应变为挂起。 (但是,标准POSIX信号不会排队,因此您只能依赖一个标准POSIX信号进行处理。)
但是,我只需要在子进程SIGSTOP时发送SIGTERM,而不是之前发送SIGCONT。
目标流程只有在继续之后才会收到SIGTERM
。这就是停止进程的行为。
有解决方法吗?
也许;这取决于要求。但是请注意,您的预期用例涉及不符合POSIX的行为(即,您希望停止的流程对除了继续或直接杀死之外的其他事情做出反应);这就是你遇到问题的直接原因。
最简单的方法是使用SIGCONT
的变体而不是SIGTERM
来控制流程的终止;例如,通过sigqueue()
,提供一个有效载荷标识符,告诉SIGCONT信号处理程序将其视为SIGTERM信号(从而区分正常的SIGCONT信号,以及SIGTERM的替身信号)。 p>
更复杂的是让进程分叉一个特殊的监视子进程,定期发送特殊的“检查挂起的SIGTERM信号” SIGCONT信号,并在父进程死亡时死掉。子进程可以通过管道连接到父进程(父进程具有写结束,子进程被读取),这样当父进程终止时,子进程上的read()
返回0,子进程可以退出太。父进程SIGCONT处理程序只需要检测子进程是否发送了信号 - si_pid
结构的siginfo_t
字段只应与子进程ID匹配 - 如果由子进程发送 - ,如果所以,检查SIGTERM是否有待处理,如果是,则处理它;否则只是举起SIGSTOP。由于竞赛窗口的许多可能性,这种方法非常脆弱 - 特别是在接收SIGCONT之后提升SIGSTOP 。 (在信号处理程序中阻塞SIGCONT是必不可少的。此外,监视子进程应该位于一个单独的进程组中,而不是连接到任何终端,以避免被针对整个进程组的SIGSTOP停止。)
请注意,应该只在信号处理程序中使用异步安全函数,并保持errno
不变,以保持一切正常工作。
为了将消息打印到标准错误,我经常使用
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
static int wrerr(const char *msg)
{
const int saved_errno = errno;
const char *end = msg;
ssize_t count;
int retval = 0;
/* Find end of string. strlen() is not async-signal safe. */
if (end)
while (*end)
end++;
while (msg < end) {
count = write(STDERR_FILENO, msg, (size_t)(end - msg));
if (count > 0)
msg += count;
else
if (count != -1) {
retval = EIO;
break;
} else
if (errno != EINTR) {
retval = errno;
break;
}
}
errno = saved_errno;
return retval;
}
不仅是异步信号安全,而且还保持errno
不变。如果成功则返回0,否则返回errno
错误代码。
如果为了清晰起见,我们稍微扩展了打印件,OP的自定义信号处理程序就变成了例如
void custom_signal_handler(int signum, siginfo_t* info, void* context) {
if (signum == SIGTERM) {
wrerr("custom_signal_handler(): SIGTERM\n");
} else
if (signum == SIGCONT) {
wrerr("custom_signal_handler(): SIGCONT\n");
}
}
请注意,在使用此功能时,一个程序根本不应使用stderr
(来自<stdio.h>
),以避免混淆。