繁体   English   中英

使用 epoll_wait 和 signalfd 处理信号

[英]Handle signals with epoll_wait and signalfd

我正在使用套接字和系统调用编写自己的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-cepoll事件发生,它被唤醒,然后我使用以下代码处理信号:

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

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

epoll_wait在被信号中断时返回-1errno == EINTR 在这种情况下,您需要从signal_fd read

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

参见man 信号 7

无论是否使用SA_RESTART ,以下接口在被信号处理程序中断后永远不会重新启动; 当被信号处理程序中断时,它们总是以错误EINTR失败:

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

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

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

所以你需要添加这样的东西,

struct sigaction action;
std::memset(&action, 0, sizeof(struct sigaction));
action.sa_handler = your_handler;
sigaction(signum, &action, NULL);

对于您希望应用程序接收中断的每个符号。 还要处理sigaction的返回值。 我的经验是,如果您使用 SIG_IGN 作为处理程序,那么您仍然会从“外部”中断像 epoll_pwait 这样的系统调用,但是当您尝试通过直接向程序发送信号来从程序本身唤醒线程时,它将无法工作线程使用pthread_kill

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

例如,

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_pwait而不先解除信号阻塞。 在 epoll_wait 返回后,您可以再次解除对信号的阻塞,以便您的处理程序可以再次被调用。

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

另请参阅signalfd(2) 手册页

通常,应使用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
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM