Sockets pselect如何在网络编程中使用信号掩码阻止信号
我目前正在研究网络编程的概念,在其中我遇到了一个函数pselect,它解决了select ie的问题。有了select,就有可能出现问题,即测试intr_标志和调用select之间,如果出现信号,如果select永远阻塞,它将丢失Sockets pselect如何在网络编程中使用信号掩码阻止信号,sockets,unix,network-programming,multiplexing,Sockets,Unix,Network Programming,Multiplexing,我目前正在研究网络编程的概念,在其中我遇到了一个函数pselect,它解决了select ie的问题。有了select,就有可能出现问题,即测试intr_标志和调用select之间,如果出现信号,如果select永远阻塞,它将丢失 if (intr_flag) handle_intr(); /* handle the signal */ if ( (nready = select( ... )) < 0) { if (errno == EINTR) { if (intr_flag)
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_标志变量之前,我们阻塞了SIGINT。调用pselect时,它会用一个空集(即零掩码)替换进程的信号掩码,然后检查描述符,可能会进入睡眠状态。但当pselect返回时,进程的信号掩码将重置为调用pselect之前的值,即SIGINT被阻止
但是在上面提到的pselect代码中,我们阻塞了信号,那么我们如何检查错误EINTR呢?由于pselect会阻止所有信号,因此当中断发生时,它会阻止中断或发送到进程。只有当pselect返回时,才能发送信号
根据前面提到注释的行,在调用pselect之前,或在第一次检查和pselect之间,或调用pselect时,中断信号仍可能发生,这与阻止中断和任何其他信号的目的相矛盾,因此应导致与select相同的竞态条件
请任何人解释这是如何可能的,因为我是新的这些概念 好的,主要思想是doing ready=pselectnfds,&readfds,&writefds,&exceptfds,timeout,&sigmask;相当于以原子方式执行以下操作: 要利用这一点,我们首先阻塞,比如SIGINT: 此时我们无法接收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:
所以,我将再一次概述这一点——在调用pselect之前,我们不会收到SIGINT。我们不需要它。一旦我们进入pselect,信号SIGINT将被解锁,因为提供给pselect的信号集为空,pselect可以被SIGINT中断。
在pselect返回时,我们再次将不会收到任何进一步的SIGINT,但如果在pselect期间发生SIGINT,则我们将根据errno be EINTR检测到这一点,如果我们检查intr_标志,我们将发现它为1。我们会明白,我们需要采取相应的行动。因此,很明显,信号处理程序可以在信号解除阻塞后立即执行其工作,后者发生在pselect调用本身中
这里最重要的细节是,如果我们没有以原子方式实现特殊的pselect调用,那么在使用通常的select调用时,我们必须按照上面代码段中的/*NB:NOTE-1*/label执行步骤。而且,由于它不是原子的,我们将有机会在两个操作之间向我们发送SIGINT,即/*NB:NOTE-1*/hint所在的位置,即在我们取消阻止信号发送之后和输入select之前。到那时,信号确实会丢失。这就是为什么我们需要一个pselect调用
总而言之,pselect的原子性是其使用的解释。
如果你对这样一个概念不太熟悉,你可以参考维基百科上的或关于计算机科学主题的专门书籍
此外,我还将提供一个关于LWN的链接,该链接更加详尽。好吧,主要思想是准备就绪=pselectnfds、&readfds、&writefds、&exceptfds、timeout和&sigmask;相当于以原子方式执行以下操作: 要利用这一点,我们首先阻塞,比如SIGINT: 此时我们无法接收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:
所以,我将再一次概述这一点——在调用pselect之前,我们不会收到SIGINT。我们不需要它。一旦我们进入pselect,信号SIGINT将被解锁,因为提供给pselect的信号集为空,pselect可以被SIGINT中断。
当pselect返回时,我们再次
sa.sa_handler = handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL);
sigemptyset(&emptyset);
ready = pselect(nfds, &readfds, NULL, NULL, NULL, &emptyset);