关于sigwait()的模糊描述

17
如果在调用sigwait()时没有等待集中的信号被挂起,线程将被暂停,直到一个或多个信号变为挂起状态。调用sigwait()时,必须阻止由集合定义的信号;否则,行为是未定义的。sigwait()对集合中的信号的信号动作的影响是未指定的。
在此处,“pending”和“block”之间的区别确实很模糊。如果信号已经在等待集中挂起,则说明它们已经被阻止了。因此,在调用sigwait()之前需要阻止集合中的信号,以便在调用时可以等待信号。
总的来说,当需要让响应异步信号运行的代码通知一个线程时,应该使用sigwait()来处理信号。或者,如果实现提供了信号量,则可以使用它们来跟踪sigwait(),或者从先前使用sigaction()注册的信号处理例程中使用它们。
sigwait()的理由是它允许程序员不必编写信号处理程序,而只需等待信号并进行任何必要的清理工作,如取消阻塞,并且可以避免使用非本地goto语句跳转回主程序。

它暂停线程的执行,直到接收到指定的信号。这难道不是合理的吗? - Ignacio Vazquez-Abrams
  1. 这里“pending”和“block”的区别是什么?
  2. 如何更清楚地选择sigwait和sigaction之间的区别?
- cpuer
2个回答

26

每个进程都有一个所谓的信号掩码(signal mask),它定义了被阻塞(blocked)的一组信号。可以使用setprocmask(2)(针对单线程代码)和pthread_sigmask(3)(针对多线程代码)来查询或设置信号掩码。

每当发生一个信号(无论是通过kill(2)raise(3)等显式方式,还是通过某些其他机制如分段错误引发SIGSEGV),都会检查该信号是否被当前信号掩码阻塞。如果该信号未被阻塞,则立即处理:如果设置了相应的信号处理程序,则调用它,否则运行默认操作(通常是异常状态下退出或忽略)。如果该信号被信号掩码阻塞,则该信号的状态被设置为挂起(pending),程序继续执行。

因此,请考虑下面的示例程序:

#include <signal.h>
#include <stdio.h>

void on_sigusr1(int sig)
{
  // Note: Normally, it's not safe to call almost all library functions in a
  // signal handler, since the signal may have been received in a middle of a
  // call to that function.
  printf("SIGUSR1 received!\n");
}

int main(void)
{
  // Set a signal handler for SIGUSR1
  signal(SIGUSR1, &on_sigusr1);

  // At program startup, SIGUSR1 is neither blocked nor pending, so raising it
  // will call the signal handler
  raise(SIGUSR1);

  // Now let's block SIGUSR1
  sigset_t sigset;
  sigemptyset(&sigset);
  sigaddset(&sigset, SIGUSR1);
  sigprocmask(SIG_BLOCK, &sigset, NULL);

  // SIGUSR1 is now blocked, raising it will not call the signal handler
  printf("About to raise SIGUSR1\n");
  raise(SIGUSR1);
  printf("After raising SIGUSR1\n");

  // SIGUSR1 is now blocked and pending -- this call to sigwait will return
  // immediately
  int sig;
  int result = sigwait(&sigset, &sig);
  if(result == 0)
    printf("sigwait got signal: %d\n", sig);

  // SIGUSR1 is now no longer pending (but still blocked).  Raise it again and
  // unblock it
  raise(SIGUSR1);
  printf("About to unblock SIGUSR1\n");
  sigprocmask(SIG_UNBLOCK, &sigset, NULL);
  printf("Unblocked SIGUSR1\n");

  return 0;
}

输出:

SIGUSR1 received!
About to raise SIGUSR1
After raising SIGUSR1
sigwait got signal: 30
About to unblock SIGUSR1
SIGUSR1 received!
Unblocked SIGUSR1

1
实际上,每个线程都有一个信号掩码,而不仅仅是每个进程,pthread_sigmask 可以在任何线程中安全使用,但如果在多线程程序中使用 sigprocmask 可能会出现异常行为。 - R.. GitHub STOP HELPING ICE
@R..,我发现另一个API sigsuspend,它似乎具有与sigwait相同的功能... - cpuer
两者之间的最大区别似乎在于,sigaction 提供了一种处理信号的方式,该方式是在进程中任何线程外部处理信号(信号捕获函数不是任何线程正常执行流程的一部分)。但是 sigwait 提供了一种处理信号的方式,使得信号可以直接在进程的一个或多个线程内部处理(处理作为线程正常执行流程的一部分完成)。这意味着使用 sigwait 对处理代码的限制较少。使用 sigaction 意味着处理代码受到“信号安全”操作的限制。我说得对吗? - Dan Moulding

4

来自 signal(7) 手册:

Signal Mask and Pending Signals
    A  signal  may  be  blocked,  which means that it will not be delivered
    until it is later unblocked.  Between the time when it is generated and
    when it is delivered a signal is said to be pending.
"Pending"和"blocked"不是互斥的。
此外,在`signal(7)`手册页中也有说明:
Synchronously Accepting a Signal
    Rather than asynchronously catching a signal via a signal  handler,  it
    is  possible to synchronously accept the signal, that is, to block exe-
    cution until the signal is delivered, at which point the kernel returns
    information about the signal to the caller.  There are two general ways
    to do this:

    * sigwaitinfo(2), sigtimedwait(2),  and  sigwait(3)  suspend  execution
      until  one  of  the signals in a specified set is delivered.  Each of
      these calls returns information about the delivered signal.

因此,sigaction()用于允许其他代码运行,直到有一个挂起的信号;而sigwait()则暂停线程的执行,直到有一个被阻塞的信号。


在调用sigwait()时,由set定义的信号应该已经被阻塞了。我们如何确保这一点?需要运行额外的代码来确保吗?还是在sigwait内部自动完成? - cpuer
另外,来自signal(7)手册页的内容: "进程中的每个线程都有独立的信号掩码,指示线程当前阻止的信号集。 线程可以使用pthread_sigmask(3)来操纵其信号掩码。 在传统的单线程应用程序中,可以使用sigprocmask(2)来操纵信号掩码。" - Ignacio Vazquez-Abrams
@cpuerпјҡжҳҜзҡ„пјҢдҪ еҝ…йЎ»дҪҝз”ЁsigprocmaskжҲ–зӯүж•ҲеҮҪж•°жқҘйҳ»еЎһе®ғ们гҖӮ "Shall"з”ЁдәҺжҢҮзӨәж ҮеҮҶеҜ№еә”з”ЁзЁӢеәҸе‘ҳзҡ„иҰҒжұӮгҖӮ - R.. GitHub STOP HELPING ICE
@Ignacio Vazquez-Abrams,如果太多的信号被阻塞,会造成内存浪费吗? - cpuer
口罩存在,无论信号是否被阻塞。如果您问的是待处理信号是否被阻塞,那么我无法告诉您;我不太了解内核的内部结构。 - Ignacio Vazquez-Abrams
显示剩余11条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接