2017-09-04 58 views
0

目前我正在网络编程中我碰到的功能PSELECT的一个(附带的概念),它解决了选择,即问题的信号。使用select(),在intr_flag的测试和选择的调用之间有问题,如果信号发生,如果永远选择块,它将会丢失。怎么做PSELECT块使用信号屏蔽在网络编程

if (intr_flag) 
handle_intr(); /* handle the signal */ 
if ((nready = select(...)) < 0) { 
if (errno == EINTR) { 
if (intr_flag) 
handle_intr(); 
} 

然而,它说,随着PSELECT,我们现在可以代码这个例子中可靠地

sigset_t newmask, oldmask, zeromask; 
sigemptyset(&zeromask); 
sigemptyset(&newmask); 
sigaddset(&newmask, SIGINT); 
sigprocmask(SIG_BLOCK, &newmask, &oldmask); /* block SIGINT */ 
if (intr_flag) //here 
handle_intr(); /* handle the signal */ 
if ((nready = pselect (... , &zeromask)) < 0) { 
if (errno == EINTR) {  //here 
if (intr_flag) 
handle_intr(); 
} 
... 
} 

,它给出的代码是可靠的解释是 - 测试intr_flag变量之前,我们阻止SIGINT。当pselect被调用时,它用空集(即零掩码)替换进程的信号掩码,然后检查描述符,可能会进入睡眠状态。但是,当pselect返回时,在调用pselect(即SIGINT被阻止)之前,进程的信号掩码会重置为其值。

但与PSELECT上面的代码中提到的,我们阻止信号那么如何才能检查错误EINTR?由于pselect会阻塞所有信号,因此当发生中断时,它应该阻止中断或被传送到进程。只有当pselect返回时才能传递信号。

根据在其前面的评论这里提及,中断或仍然可能发生信号PSELECT被调用之前在两者之间的第一检查和PSELECT或当PSELECT称为矛盾阻挡中断的目的,行任何其他信号,因此应该导致竞争条件,因为选择在那里。

请谁能解释这是怎么可能的,因为我是新来的这些概念。

回答

1

好了,主要的想法是,这样做ready = pselect(nfds, &readfds, &writefds, &exceptfds, timeout, &sigmask);相当于执行以下操作原子

sigset_t sigsaved; 

sigprocmask(SIG_SETMASK, &sigmask, &sigsaved); 
/* NB: NOTE-1 */ 
ready = select(nfds, &readfds, &writefds, &exceptfds, timeout); 
sigprocmask(SIG_SETMASK, &sigsaved, NULL); 

要利用这一点,我们第一块,比方说,SIGINT

sigset_t emptyset, blockset; 

sigemptyset(&blockset); 
sigaddset(&blockset, SIGINT); 
sigprocmask(SIG_BLOCK, &blockset, NULL); 

那时我们不能收到SIGINT,因此我们无法处理它。但我们不需要它,直到我们输入pselect()。我们想要做的 我们已经封锁SIGINT后什么是设置一个适当的信号处理程序。我们有一个标志static volatile int intr_flag = 0;在我们的主代码之外声明,我们定义了一个名为handler()的处理程序,它只是简单地intr_flag = 1;。因此,我们将其设置为一个处理程序:

sa.sa_handler = handler; 
sa.sa_flags = 0; 
sigemptyset(&sa.sa_mask); 
sigaction(SIGINT, &sa, NULL); 

然后我们配置readfs(声明没有显示在这里), 初始化一个空的信号设定和调用pselect()

sigemptyset(&emptyset); 
ready = pselect(nfds, &readfds, NULL, NULL, NULL, &emptyset); 

因此,我将概述这一个更多的时间 - 我们没有收到SIGINT,直到我们叫pselect()。我们不需要它。只要我们输入pselect(),信号SIGINT将被解除阻塞(因为提供给pselect()的空信号集),并且pselect()可以被SIGINT中断。 而此时pselect()回报,我们再不会在pselect()收到任何进一步SIGINT,但如果SIGINT发生点,那么我们将其检测为每errnoEINTR,如果我们检查intr_flag,我们会发现它是1。我们会理解,我们需要相应的行为。所以,很明显信号处理器可以在信号解除阻塞后立即完成工作,而后者发生在pselect()调用本身内。

最重要的这里的细节是,如果我们没有特殊pselect()呼叫原子方式来实现,那么我们有使用普通select()时做上述自己的片断周围/* NB: NOTE-1 */标签的步骤。而且,因为它不会是原子,我们将有机会在两个操作之间将SIGINT交付给我们 - 其中/* NB: NOTE-1 */提示所在位置,即在我们解除信号传递并且在输入select()之前。那时信号确实会丢失。这就是为什么我们需要拨打pselect()

总而言之,原子性pselect()是其使用的解释。 如果你对这个概念不太熟悉,你可以参考维基百科上的article或计算机科学主题的专门书籍。

此外,我会提供我的答案,连接到LWN上的article,这是更详尽的。

+0

很好的解释:)谢谢! –

+0

不客气。 –