我正在尝试用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
发布于 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()并自己选择()。
例如:
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");
}
}
你还应该对你的代码做一些其他的事情:
sig_atomic_t shouldQuit =0;
实际上,我强烈建议使用现有的事件循环处理库,如libev或libevent -我确实这样做了,尽管我可以编写自己的库,因为它很容易出错。
https://stackoverflow.com/questions/14045801
复制相似问题