发布
社区首页 >问答首页 >OSX和Linux上的pselect()行为不同?

OSX和Linux上的pselect()行为不同?
EN

Stack Overflow用户
提问于 2012-12-27 04:21:12
回答 1查看 966关注 0票数 2

我正在尝试用pselect实现一个基本的事件循环,所以我阻塞了一些信号,保存了信号掩码,并将其与pselect一起使用,这样信号只会在调用期间传递。

如果一个信号被发送到pselect调用之外,它将被阻塞,直到正常执行pselect,但是它不会中断pselect调用。如果在pselect阻塞时发送信号,它将被处理,pselect将被中断。这种行为只在OSX中存在,在linux中它似乎可以正常工作。

下面是一个代码示例:

代码语言:javascript
代码运行次数:0
复制
#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上:

代码语言:javascript
代码运行次数:0
复制
Starting pselect
a
Done pselect
Start Sleeping
^CDone Sleeping
Starting pselect
Handled signal 2
Done pselect

在OSX上:

代码语言:javascript
代码运行次数:0
复制
Starting pselect
a
Done pselect
Start Sleeping
^CDone Sleeping
Starting pselect
Handled signal 2
^CHandled signal 2
Done pselect
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-12-27 06:27:16

如果你查看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曾经使用过类似的用户空间实现,但也有同样的问题。

如果您想使该模式在所有平台上都是安全的,则必须使用libev或libevent之类的工具并让它们处理混乱,或者使用sigprocmask()并自己选择()。

例如:

代码语言:javascript
代码运行次数:0
复制
    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‘变量声明为 sig_atomic_t

sig_atomic_t shouldQuit =0;

  • 总是在调用任何其他函数(如printf())之前保存errno,因为该函数可能会导致errno被其他值覆盖。这就是为什么上面的代码在select()调用之后立即aves。

实际上,我强烈建议使用现有的事件循环处理库,如libev或libevent -我确实这样做了,尽管我可以编写自己的库,因为它很容易出错。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14045801

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档