【问题标题】:blocking function accept() restarts when SIGINT occurs even though SA_RESTART flag is set to zero即使 SA_RESTART 标志设置为零,当 SIGINT 发生时,阻塞函数 accept() 也会重新启动
【发布时间】:2016-08-07 07:06:16
【问题描述】:

我正在 Linux 上编写一个 C 程序,其中我有一个 main() 和两个由它创建的 pthread。在其中一个 pthread 中,我调用了 accept() 函数。

我有一个在收到 SIGINT、SIGQUIT 或 SIGTERM 时调用的信号处理程序。

我的期望是,因为我将 SA_RESTART 标志设为零,所以当我按下 ctrl-c 时,accept() 函数应该返回 EINTR 而不是重新启动,但是我在调​​试时通过一堆 printf 调用实现了(查看哪些行是通过打印代码所在的位置执行),即使我的应用程序能够捕获 SIGINT,accept 函数仍然被阻止,它不会因 EINTR 而失败并且不会移动到下一行代码。 这是我在 main() 中的设置

struct sigaction signal_action;
signal_action.sa_flags = 0; // Don't restart the blocking call after it failed with EINTR
signal_action.sa_handler = terminate;
sigemptyset(&signal_action.sa_mask);
sigfillset(&signal_action.sa_mask); // Block every signal during the handler is executing
if (sigaction(SIGINT, &signal_action, NULL) < 0) {
    perror("error handling SIGINT");
}
if (sigaction(SIGTERM, &signal_action, NULL) < 0) {
    perror("error handling SIGTERM");
}
if (sigaction(SIGQUIT, &signal_action, NULL) < 0) {
    perror("error handling SIGQUIT");
}

这是信号处理程序:

void terminate (int signum)
{
    terminate_program = 1;
    printf("Terminating.\n");
}

这是进行 accept() 调用的 pthread(我试图删除不相关的内容以使我的问题更容易理解):

void* pthread_timerless_socket_tasks(void* parameter)
{

    server_socket_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(server_socket_fd < 0)
    {
        perror("error creating IPv4 TCP stream socket");
        return (NULL);
    }
    printf("socket created\n"); // for debugging
    if(fcntl(server_socket_fd, F_SETFL, 0) < 0)
    {
        perror("error making socket_fd blocking");
        close(server_socket_fd);
        return (NULL);
    }

    while(!terminate_program)
    {
        printf("socket blocking on accept\n"); // for debugging
        client_socket_fd = accept(server_socket_fd,(struct sockaddr *) &client_address, &client_length);
        printf("socket accepted?\n"); // for debugging
        if(client_socket_fd < 0)
        {
            perror("error accepting socket_fd");
            close(server_socket_fd);
            return (NULL);
        }


    } 

我希望我说清楚了。

所以,现在我想知道缺少什么或不正确,因为我看不到 SA_RESTART 的 linux 手册中描述的行为。

【问题讨论】:

  • 这个if(fcntl(server_socket_fd, F_SETFL, 0) ... 看起来很奇怪,不要说错。
  • @alk 我试图让文件描述符以这种方式阻塞,因为默认情况下它不是。我愿意接受建议:)
  • 关于如何使用fcntl() 切换到套接字的阻塞状态,您可能希望看到这个答案:stackoverflow.com/a/1549344/694576

标签: c linux pthreads signals blocking


【解决方案1】:

来自the signal(7) manual page

如果多个线程解除了信号阻塞,那么内核会选择一个任意线程来传递信号。

这意味着如果信号没有发送到执行accept的线程,那么调用不会被信号中断。

你应该阻塞(通过设置信号掩码)所有其他线程中的信号,那么唯一可以接收信号的线程就是调用accept的线程。

【讨论】:

  • 我将“设置”代码(我调用 sigaction 并将 sa_flags 设置为零)从 main() 移动到带有 accept() 的线程(并确保没有其他线程有此代码) , 以确保信号被传递到正确的线程但仍然 accept() 阻塞。
  • 我从你的帖子中意识到,我也需要阻止其他线程,我现在试试。
  • @etugcey 打开链接的手册页,然后搜索“信号掩码和未决信号”。这将告诉您每个进程和每个线程需要阻止和解除阻止信号的功能。
  • @etugcey 欢迎使用 pthreads。干净地阻塞系统调用来停止线程是最困难的问题之一。您的下一个想法是查看pthread_cancel。忽略那个。它会给你带来更多的麻烦。我发现做到这一点的最好方法是不要阻塞 poll 以外的任何东西,并让每个线程 poll 在文件描述符上(linux 上的 eventfd,其他所有系统上的管道),同时轮询另一个文件描述符(就像你的监听套接字)并使用它来向线程发出他们需要退出(或做其他事情)的信号。
  • @art ... 或安装一个(进程范围的)线程来捕获所有信号,并让后者根据需要使用pthread_kill() 调度接收到的信号。
猜你喜欢
  • 2022-01-20
  • 1970-01-01
  • 2012-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-09
  • 2011-10-03
  • 1970-01-01
相关资源
最近更新 更多