在Linux上如何执行asynchronous信号处理程序?

我想知道如何在Linux上执行asynchronous信号处理程序。 首先,我不清楚哪个线程执行信号处理程序。 其次,我想知道使线程执行信号处理程序的步骤。

首先,我读了两个不同的,看似矛盾的解释:

  1. 由Andries Brouwer 撰写的Linux Kernel第5.2节“接收信号”指出 :

    当信号到达时,进程被中断,当前的寄存器被保存,信号处理程序被调用。 当信号处理程序返回时,中断的活动继续。

  2. StackOverflow问题“处理multithreading程序中的asynchronous信号”使我认为Linux的行为就像SCO Unix一样 :

    当一个信号被传送到一个进程时,如果它被捕获,它将被满足以下任一条件的线程中的一个,并且只处理一个:

    1. sigwait (2)系统调用中阻塞的线程,其参数确实包含捕获信号的types。

    2. 信号掩码包含捕获信号的types的线程。

    其他注意事项

    • 被阻塞在sigwait (2)中的线程优先于不阻塞信号types的线程。
    • 如果多个线程满足这些要求(也许两个线程调用sigwait (2) ),则会select其中一个线程。 这个select是不可预测的应用程序。
    • 如果没有线程符合条件,则信号将在进程级别保持“挂起”状态,直到某个线程符合条件。

    此外, Moshe Bar的“Linux信号处理模型”指出: “asynchronous信号被传递到第一个线程,而不是阻塞信号”,我解释为这个信号被传递给某个线程,其信号的sigmask 包括信号。

哪一个是正确的?

关于第二个问题,堆栈和选定线程的内容会发生什么变化? 假设线程运行信号处理程序T正在执行一个do_stuff()函数。 线程T的堆栈是否直接用来执行信号处理程序(即信号蹦床的地址被推送到T的堆栈上,控制stream程到达信号处理程序)? 另外,是一个单独的堆栈使用? 它是如何工作的?

如果考虑到Linux黑客往往对线程和进程之间的差异感到困惑,这两个解释并不矛盾,主要是因为尝试假冒线程可能被实现为共享进程的历史错误记忆。 🙂

说了这么多,解释#2更加详细,完整和正确。

至于堆栈和寄存器内容,每个线程可以注册自己的备用信号处理堆栈,并且该进程可以基于每个信号select哪些信号将在交替的信号处理堆栈上被递送。 中断的上下文(寄存器,信号掩码等)将被保存在线程堆栈上的ucontext_t结构中,以及蹦床返回地址。 使用SA_SIGINFO标志安装的信号处理程序能够检查这个ucontext_t结构,但是他们唯一可以做的便携事件是检查(并可能修改)保存的信号掩码。 (我不确定是否修改它是由标准认可的,但它是非常有用的,因为它允许信号处理程序在返回时自动replace被中断的代码的信号掩码,例如让信号被阻塞,所以不会再发生。)