从单独的线程调用时,pselect不会返回信号,但是在单线程程序中可以正常工作 [英] pselect does not return on signal when called from a separate thread but works fine in single thread program

查看:85
本文介绍了从单独的线程调用时,pselect不会返回信号,但是在单线程程序中可以正常工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习如何使用pselect.我以示例代码为例,该代码运行良好,并对其进行了修改,以从从main派生的线程中调用相同的代码,并且该代码不起作用(pselect永远被阻止)

I am learning how to use pselect. I took an example code which worked fine and modified it to call the same code from a thread which is spawned from main and it does not work (pselect remains blocked forever)

#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

/* Flag that tells the daemon to exit. */
static volatile int exit_request = 0;

/* Signal handler. */
static void hdl (int sig)
{
    exit_request = 1;
    printf("sig=%d\n", sig);
}

/* Accept client on listening socket lfd and close the connection
 * immediatelly. */
static void handle_client (int lfd)
{
    int sock = accept (lfd, NULL, 0);
    if (sock < 0) {
        perror ("accept");
        exit (1);
    }

    puts ("accepted client");

    close (sock);
}

void *mythread(void *arg  __attribute__ ((unused)))
{
    int lfd;
    struct sockaddr_in myaddr;
    int yes = 1;
    sigset_t mask;
    sigset_t orig_mask;
    struct sigaction act;

    memset (&act, 0, sizeof(act));
    act.sa_handler = hdl;

    /* This server should shut down on SIGUSR1. */
    if (sigaction(SIGUSR1, &act, 0)) {
        perror ("sigaction");
        return NULL;
    }

    sigemptyset (&mask);
    sigaddset (&mask, SIGUSR1);

    if (pthread_sigmask(SIG_BLOCK, &mask, &orig_mask) < 0) {
        perror ("pthread_sigmask");
        return NULL;
    }

    lfd = socket (AF_INET, SOCK_STREAM, 0);
    if (lfd < 0) {
        perror ("socket");
        return NULL;
    }

    if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR,
                &yes, sizeof(int)) == -1) {
        perror ("setsockopt");
        return NULL;
    }

    memset (&myaddr, 0, sizeof(myaddr));
    myaddr.sin_family = AF_INET;
    myaddr.sin_addr.s_addr = INADDR_ANY;
    myaddr.sin_port = htons (10000);

    if (bind(lfd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) {
        perror ("bind");
        return NULL;
    }

    if (listen(lfd, 5) < 0) {
        perror ("listen");
        return NULL;
    }

    while (!exit_request) {
        fd_set fds;
        int res;

        /* BANG! we can get SIGUSR1 at this point, but it will be
         * delivered while we are in pselect(), because now
         * we block SIGUSR1.
         */

        FD_ZERO (&fds);
        FD_SET (lfd, &fds);

        res = pselect (lfd + 1, &fds, NULL, NULL, NULL, &orig_mask);
        if (res < 0 && errno != EINTR) {
            perror ("select");
            return NULL;
        }
        else if (exit_request) {
            puts ("exited");
            break;
        }
        else if (res == 0)
            continue;

        if (FD_ISSET(lfd, &fds)) {
            handle_client (lfd);
        }
    }

    return NULL;
}
int main (int argc, char *argv[])
{
    void * res;
    pthread_t mythr_h;
    pthread_create(&mythr_h, (pthread_attr_t *)NULL, mythread, NULL);
    pthread_join(mythr_h, &res);
    return 0;
}

强文本

将SIGUSR1发送到此程序后,我发现它在pselect调用中仍然处于阻塞状态.当mythread函数中的代码移回main且未从main产生任何线程时,它可以完美工作.

After sending SIGUSR1 to this program I see that it remains blocked in the pselect call. When the code in mythread function is moved back into main and not spawning any thread from main, it works perfectly.

推荐答案

除了pselect()调用上的线程阻塞之外,信号还传递到主线程.如果有多个线程使信号畅通无阻,则可以将信号传递到任何一个线程.

The signal is delivered to the main thread, other than the thread blocking on the pselect() call. If there are multiple threads that have the signal unblocked, the signal can be delivered to any one of the threads.

由于您未指定平台,因此首先引用 POSIX标准(系统接口卷,2.4.1信号生成和传递).

Since you didn't specify your platform, first I'm quoting from the POSIX standard (System Interfaces volume, 2.4.1 Signal Generation and Delivery).

为该过程生成的信号应准确地传递到该过程中的那些线程之一,该线程在调用sigwait()函数的调用中选择该信号或​​没有阻止信号的传递.

Signals generated for the process shall be delivered to exactly one of those threads within the process which is in a call to a sigwait() function selecting that signal or has not blocked delivery of the signal.

您还可以在Linux手册页 signal(7) .

You can also see similar statements in Linux manpage signal(7).

过程控制信号可以传递给任何 当前未阻止信号的线程之一. 如果多个线程之一的信号畅通无阻,则 内核选择向其传递信号的任意线程.

A process-directed signal may be delivered to any one of the threads that does not currently have the signal blocked. If more than one of the threads has the signal unblocked, then the kernel chooses an arbitrary thread to which to deliver the signal.

FreeBSD联机帮助页 sigaction(2) .

And FreeBSD manpage sigaction(2).

对于定向到该过程的信号,如果 信号当前未被所有线程阻止,然后将其传递给 一个没有被阻塞的线程(该线程的选择是 未指定).

For signals directed at the process, if the signal is not currently blocked by all threads then it is delivered to one thread that does not have it blocked (the selection of which is unspecified).

因此,您可以为所有进程中的所有线程阻塞SIGUSR1,除了那些调用pselect()的线程.幸运的是,创建新线程时,它会从其创建者那里继承信号掩码.

So what you can do is to block SIGUSR1 for all the threads in the process except for the one that calls pselect(). Luckily when a new thread is created, it inherits the signal mask from its creator.

与上面的POSIX部分相同,

From the same POSIX section above,

线程的信号掩码应从其父线程或正在创建线程的信号掩码中初始化.

The signal mask for a thread shall be initialized from that of its parent or creating thread....

Linux pthread_sigmask(3)

Linux pthread_sigmask(3),

新线程继承了其创建者的信号掩码的副本.

A new thread inherits a copy of its creator's signal mask.

FreeBSD sigaction(2)

线程的信号掩码从其父线程的信号掩码初始化(通常为空).

The signal mask for a thread is initialized from that of its parent (normally empty).

您可以对代码进行以下更改.在main()中,在创建任何线程之前,先阻塞SIGUSR1.在线程中,将具有SIGUSR1未被阻塞的信号掩码传递到pselect().

You can make the following changes to your code. In main(), before creating any threads, block SIGUSR1. In the thread, pass a signal mask that has SIGUSR1 unblocked into pselect().

--- old.c   Mon Mar 21 22:48:52 2016
+++ new.c   Mon Mar 21 22:53:54 2016
@@ -56,14 +56,14 @@
         return NULL;
     }

-    sigemptyset (&mask);
-    sigaddset (&mask, SIGUSR1);
-
-    if (pthread_sigmask(SIG_BLOCK, &mask, &orig_mask) < 0) {
+    sigemptyset(&orig_mask);
+    if (pthread_sigmask(SIG_BLOCK, NULL, &orig_mask) < 0) {
         perror ("pthread_sigmask");
         return NULL;
     }

+    sigdelset(&orig_mask, SIGUSR1);
+
     lfd = socket (AF_INET, SOCK_STREAM, 0);
     if (lfd < 0) {
         perror ("socket");
@@ -126,6 +126,15 @@
 {
     void * res;
     pthread_t mythr_h;
+    sigset_t mask;
+
+    sigemptyset (&mask);
+    sigaddset (&mask, SIGUSR1);
+
+    if (pthread_sigmask(SIG_BLOCK, &mask, NULL) != 0) {
+        return 1;
+    }
+
     pthread_create(&mythr_h, (pthread_attr_t *)NULL, mythread, NULL);
     pthread_join(mythr_h, &res);
     return 0;

最后一件事是题外话. printf()不是async-signal-safe函数,因此不应在信号处理程序中调用.

Last thing is off topic. printf() is not an async-signal-safe function, so should not be called in the signal handler.

这篇关于从单独的线程调用时,pselect不会返回信号,但是在单线程程序中可以正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆