使用epoll_wait处理信号

时间:2017-04-04 15:55:24

标签: c sockets signals epoll

我正在使用套接字和系统调用编写自己的echo server。我使用epoll同时与许多不同的客户端一起工作,所有与客户端一起完成的操作都是非阻塞的。当服务器打开并且什么都不做时,它在epoll_wait中。现在我想添加使用信号关闭服务器的可能性。例如,我在bash terminal中启动服务器,然后按ctrl-c,服务器以某种方式处理SIGINT。我的计划是使用signalfd。我创建了新的signalfd并使用以下代码将其添加到epoll实例:

    sigset_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGTERM);
    sigaddset(&mask, SIGINT);
    signal_fd = signalfd(-1, &mask, 0);

    epoll_event event;
    event.data.fd = signal_fd;
    event.events = EPOLLIN;
    epoll_ctl(fd, EPOLL_CTL_ADD, signal_fd, &event);

然后我希望,当epoll等待并按ctrl-c时,epoll上的事件发生,它会唤醒然后我使用以下代码处理信号:

    if (events[i].data.fd == signal_fd)
    {
        //do something
        exit(0);
    }

虽然实际上服务器只是在没有处理信号的情况下停止。我做错了什么,解决问题的正确方法是什么?如果我没有正确理解信号,那么应该使用signalfd的地方是什么?

3 个答案:

答案 0 :(得分:3)

当信号中断时,

epoll_wait会返回-1errno == EINTR。在这种情况下,您需要read中的signal_fd

将信号的信号处理程序设置为SIG_IGN,否则信号可能会终止您的应用程序。

请参阅man signal 7

  

以下接口在被中断后永远不会重新启动          信号处理程序,无论SA_RESTART的用途如何;他们总是失败          在被信号处理程序中断时出现错误EINTR

     
      
  • 文件描述符多路复用接口:epoll_wait(2),        epoll_pwait(2),poll(2),ppoll(2),select(2)和pselect(2)。
  •   

答案 1 :(得分:0)

  

尽管实际上服务器只是在不处理信号的情况下停止。我做错了什么,解决问题的正确方法是什么?如果我对信号的理解不正确,应该在哪里使用signalfd?

信号处理程序是每个进程的。您将信号处理程序保留为默认值,即终止进程。

所以您需要添加类似的内容

<div class='formContainer'>
  <label>
     <span>Name</span>
  </label>
  <div>
    <input type="text" />
  </div>
  <div class='matches'>
    <div>No matches yet!</div>
  </div>
</div>

对于每个您希望应用程序接收中断的信号。还处理sigaction的返回值。我的经验是,如果您使用SIG_IGN作为处理程序,则您仍然会从“外部”中断系统调用,例如epoll_pwait,但是当您尝试通过直接向其发送信号从程序本身唤醒线程时,它将无法工作使用struct sigaction action; std::memset(&action, 0, sizeof(struct sigaction)); action.sa_handler = your_handler; sigaction(signum, &action, NULL); 的线程。

接下来,您需要屏蔽来自每个线程的所有信号,以便默认情况下没有线程会接收到它(否则将唤醒一个随机线程来处理该信号)。最简单的方法是在创建任何线程之前先在main中进行操作。

例如,

pthread_kill

然后,当您希望每个线程接收信号时,取消阻塞每个线程的信号。

如果您使用sigset_t all_signals; sigemptyset(&all_signals); sigaddset(&all_signals, signum); // Repeat for each signum that you use. sigprocmask(SIG_BLOCK, &all_signals, NULL); ,则不想取消阻止它们-该系统调用会取消阻止信号本身,只需传递适当的掩码(为signalfd设置位(它使用传递的掩码来解锁)。另请参见signalfd的手册页。

signalfd的工作方式有所不同;像epoll_pwait一样,您可以解除对您感兴趣的信号的阻塞。您可以为该信号设置一个处理程序(参见上文),该处理程序可以设置一个标志。然后,在调用pselect之前,您要阻塞信号,然后测试该标志并对其进行处理,然后在不首先取消阻塞该信号的情况下调用epoll_pwait。在epoll_wait返回之后,您可以再次解除阻止信号,以便可以再次调用您的处理程序。

答案 2 :(得分:0)

在创建信号 FD 之前,您必须阻止所有要使用信号 FD 处理的信号。否则,这些信号仍会中断被阻塞的系统调用,例如 epoll_wait() - 正如您所观察到的。

另见signalfd(2) man page

<块引用>

通常,要通过文件描述符接收的信号集 应使用 sigprocmask(2) 阻止,以防止信号被 按照他们的默认处理方式进行处理。

因此,您必须像这样更改示例:

    sigset_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGTERM);
    sigaddset(&mask, SIGINT);
    int r = sigprocmask(SIG_BLOCK, &mask, 0);
    if (r == -1) {
        // XXX handle errors
    }
    signal_fd = signalfd(-1, &mask, 0);
    if (signal_fd == -1) {
        // XXX handle errors
    }

    epoll_event event;
    event.data.fd = signal_fd;
    event.events = EPOLLIN;
    r = epoll_ctl(fd, EPOLL_CTL_ADD, signal_fd, &event);
    if (r == -1) {
        // XXX handle errors
    }