当进程在waitpid中等待时如何处理信号?

时间:2017-04-19 06:24:20

标签: c signals

我有一个带有用户定义的信号处理程序的父进程。每当收到信号时,我设置一个全局变量,在我的主循环中,我在每次输入处理之前检查信号是否被接收。

我产生了一个处理用户输入的进程,并使用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?

的答案

1 个答案:

答案 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$