我试图用pselect实现一个基本的事件循环,因此我屏蔽了一些信号,保存了信号掩码,并将其与pselect一起使用,以便信号只在调用期间传递。
如果信号发送到pselect调用之外,它将被阻塞,直到pselect为止,但它不会中断pselect调用如果在pselect被阻塞时发送了一个信号,它将被处理并且pselect将被中断这种行为只存在于OSX中,在linux中,它似乎运行正常。
下面是一个代码示例:

#include <stdio.h>
#include <string.h>
#include <sys/select.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>

int shouldQuit = 0;

void signalHandler(int signal)
{
    printf("Handled signal %d\n", signal);
    shouldQuit = 1;
}

int main(int argc, char** argv)
{
    sigset_t originalSignals;
    sigset_t blockedSignals;
    sigemptyset(&blockedSignals);
    sigaddset(&blockedSignals, SIGINT);
    if(sigprocmask(SIG_BLOCK, &blockedSignals, &originalSignals) != 0)
    {
        perror("Failed to block signals");
        return -1;
    }

    struct sigaction signalAction;
    memset(&signalAction, 0, sizeof(struct sigaction));

    signalAction.sa_mask = blockedSignals;

    signalAction.sa_handler = signalHandler;

    if(sigaction(SIGINT, &signalAction, NULL) == -1)
    {
        perror("Could not set signal handler");
        return -1;
    }

    while(!shouldQuit)
    {
        fd_set set;
        FD_ZERO(&set);
        FD_SET(STDIN_FILENO, &set);
        printf("Starting pselect\n");
        int result = pselect(STDIN_FILENO + 1, &set, NULL, NULL, NULL, &originalSignals);
        printf("Done pselect\n");
        if(result == -1)
        {
            if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
            {
                perror("pselect failed");
            }
        }
        else
        {
            printf("Start Sleeping\n");
            sleep(5);
            printf("Done Sleeping\n");
        }
    }

    return 0;
}

程序会一直等到你在stdin上输入一些内容,然后休眠5秒为了解决这个问题,输入“a”在stdin上创建数据然后,当程序处于睡眠状态时,用Crtl-C发送一个INT信号。
在Linux上:
Starting pselect
a
Done pselect
Start Sleeping
^CDone Sleeping
Starting pselect
Handled signal 2
Done pselect

在OSX上:
Starting pselect
a
Done pselect
Start Sleeping
^CDone Sleeping
Starting pselect
Handled signal 2
^CHandled signal 2
Done pselect

最佳答案

确认了它在OSX上的作用,如果你查看pselect的源代码(http://www.opensource.apple.com/source/Libc/Libc-320.1.3/gen/FreeBSD/pselect.c),你就会明白原因了。
在sigprocmask()恢复信号掩码之后,内核将信号传递给进程,并调用处理程序这里的问题是,可以在调用select()之前传递信号,因此select()不会返回错误。
http://lwn.net/Articles/176911/-linux上有更多关于这个问题的讨论,linux使用了一个类似的用户空间实现,这个实现也有同样的问题。
如果要在所有平台上确保该模式的安全性,则必须使用libev或libevent之类的工具并让它们处理混乱,或者自己使用sigprocmask()和select()。
例如

    sigset_t omask;
    if (sigprocmask(SIG_SETMASK, &originalSignals, &omask) < 0) {
        perror("sigprocmask");
        break;
    }

    /* Must re-check the flag here with signals re-enabled */
    if (shouldQuit)
        break;

    printf("Starting select\n");
    int result = select(STDIN_FILENO + 1, &set, NULL, NULL, NULL);
    int save_errno = errno;
    if (sigprocmask(SIG_SETMASK, &omask, NULL) < 0) {
        perror("sigprocmask");
        break;
    }

    /* Recheck again after the signal is blocked */
    if (shouldQuit)
        break;

    printf("Done pselect\n");

    if(result == -1)
    {
        errno = save_errno;
        if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
        {
            perror("pselect failed");
        }
    }

您还应该对代码执行其他一些操作:
将“shouldQuit”变量声明为volatile sig_atomic
易失性sig_-atomic_t shouldQuit=0;
在调用任何其他函数(如printf())之前,请始终保存errno,因为该函数可能会导致用其他值覆盖errno这就是为什么上面的代码在select()调用之后立即出现错误。
实际上,我强烈建议使用一个现有的事件循环处理库,比如LiBeV或LiBevun-I,即使我可以自己编写,因为它很容易出错。

关于c - OSX与Linux上不同的pselect()行为?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14045801/

10-12 02:56