我有一个带有用户定义的信号处理程序的父进程。每当收到信号时,我设置一个全局变量,在我的主循环中,我在每次输入处理之前检查信号是否被接收。
我产生了一个处理用户输入的进程,并使用waitpid来收集子等待状态。同时,如果父进程收到用户定义的信号,如何有效地处理它?
从我的应用程序中最小化代码片段
#include <stdio.h>
#include <signal.h>
volatile sig_atomic_t g_signal_number =0;
void sig_hup_handler()
{
g_signal_number = SIGHUP;
}
void do_process_cleanup() {
//Execute
}
int execute_user_input(char *str)
{
pid_t cpid,w;
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { //Child may take 10-20 second to process the string.
execute_command(str);
_exit(0);
} else {
w = waitpid(cpid, &status, 0);
}
}
main()
{
char str[1024];
signal(SIGHUP, sig_int_handler);
while(1) {
if(g_signal_number)
{
do_process_cleanup();
break;
}
get_user_input(str);
if(!strcmp(str,"end"))
break;
execute_user_input(str);
}
exit(0;)
}
我尝试使用以下代码来解决此问题。有人可以建议一个更好的方法来处理这个问题吗?
while (1) {
int ret = waitpid(cpid, &status, WNOHANG);
if(ret > 0)
break;
if(ret < 0)
if(errno == EINTR)
continue;
if(g_signal_number) // break the loop if signal recieved
break
sleep(1);
}
还尝试使用SA_RESTART标志,但它在我的Linux风格中没有用于waitpid系统调用。
int main() {
:
sa.sa_handler = sig_hup;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
:
while(1) {
ret=waitpid(child_pid, &status, 0);
/*
*if waipid returns -1 and errno is set to
*EINTR and g_signum is set,
*break the loop.
*/
if(ret == -1 && errno == EINTR && g_signum) {
printf("signal is set, so break the loop\n");
break;
}
printf("waitpid restart...\n");
}
从strace输出我可以看到,在将SIGHUP发送到PID 17618后,waitpid也不会中断。
-bash-4.1$ strace -p 17618
Process 17618 attached
wait4(17619, 0x7ffc4a2e2c34, 0, NULL) = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
--- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=26521, si_uid=332691} ---
rt_sigreturn() = 61
wait4(17619,
环境:类似RedHat的发行版,2.6.39内核
更新
如果有人以后遇到此问题,请参阅Why doesn't Linux accept() return EINTR?
的答案答案 0 :(得分:0)
SA_RESTART处理的方式是定义的位实现。我的Linux风格(RedHat-like发行版,2.6.39内核)不服从wait_id系统调用的SA_RESTART。
但是我可以通过在信号处理程序中设置sigaction来解决这个问题。
/*
*test_signal.c
*/
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
static struct sigaction sa;
/****Set this variable when SIGHUP received****/
volatile sig_atomic_t g_signum = 0;
/****SIGHUP HANDLER****/
void sig_hup(int signum) {
/****Set the signal number****/
g_signum = signum;
/****Time to intrupt the system call****/
sigaction(SIGHUP, &sa, 0);
}
int main() {
int status,ret;
pid_t child_pid;
sa.sa_handler = sig_hup;
sigemptyset(&sa.sa_mask);
sigaction(SIGHUP, &sa, 0);
child_pid = fork();
if(child_pid == 0) {
sleep(1);//Let the parent print the msg first
printf("pid:%d:child going to sleep in a loop\n",getpid());
while(1) {
sleep(10);
}
}
else{
printf("parent pid: %d child pid: %d\n",getpid(),child_pid);
while(1) {
ret=waitpid(child_pid, &status, 0);
/*
*if waitpid returns -1 and errno is set to
*EINTR and g_signum is set,
*break the loop.
*/
if(ret == -1 && errno == EINTR && g_signum) {
printf("signal is set, so break the loop\n");
break;
}
printf("waitpid restart...\n");
}
}
return 0;
}
输出:
-bash-4.1$ ./test_signal
parent pid: 11122 child pid: 11123
pid:11123:child going to sleep in a loop
signal is set, so break the loop
-bash-4.1$
将SIGHUP发送给父母:
-bash-4.1$ kill -s SIGHUP 11122
Strace输出:
-bash-4.1$ strace -p 11122
Process 11122 attached
wait4(11123, 0x7ffca4b72c24, 0, NULL) = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
--- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=26521, si_uid=332691} ---
rt_sigaction(SIGHUP, {0x400714, [], SA_RESTORER, 0x33c6432660}, NULL, 8) = 0
rt_sigreturn() = -1 EINTR (Interrupted system call)
write(1, "signal is set, so break the loop"..., 33) = 33
exit_group(0) = ?
+++ exited with 0 +++
-bash-4.1$