在linux中执行信号处理程序是不可抢占的?

时间:2011-05-25 17:40:58

标签: multithreading operating-system signal-handling

我在p的信号处理程序中注册了一个进程SIGALRM。设置定时器以周期性地将信号SIGALRM发送到进程p。在进程p中还有多个线程在运行。信号处理程序在被触发和执行时是否是不可抢占的?或者说,是否信号处理程序的执行不会被进程p中的任何线程中断?

PS:我认为信号处理程序是在内核中执行的(是吗?)而内核对于用户模式线程是不受限制的?纠正我,如果这是错的......

3 个答案:

答案 0 :(得分:17)

非常 - - 在信号处理程序中处理共享数据几乎总会导致痛苦的世界,同时处理线程并且让你自己弄得一团糟。

默认情况下,信号处理程序运行时会阻塞信号(至少在linux上,这可能不是普遍存在的),所以至少信号处理程序不会被自己抢占。但是,如果你有多个线程,并且信号没有在其他线程中被阻塞,那么信号处理程序很可能会在多个线程中同时运行。

一个线程将接收信号并执行处理程序,它或多或少是随机的线程,尽管你可以通过阻止你不想处理信号的所有线程中的信号来控制它。

但是,处理信号的任何其他线程都可以并行运行。处理信号的线程可以在程序中的几乎任何点运行信号处理程序(只要信号没有被阻塞)。因此,您需要某种锁定来保护该数据。问题是你不能使用任何普通的线程锁定原语,它们不是信号异步安全。这意味着你,例如尝试在信号处理程序中获取pthread_mutex_t,您很容易使程序死锁。

您可以在信号处理程序中安全调用的唯一函数是here列出的函数。 关于保护共享数据,您可以使用sigblock()/ sigunblock()作为一种保护,确保在您访问共享数据时信号处理程序不会运行 - 并且信号必须在所有线程,否则它只会在其中一个没有被阻塞的线程中运行 - 走这条路是疯狂的。

您可以在信号处理程序中安全访问的唯一共享数据是sig_atomic_t类型,实际上其他类型的原始类型通常也是安全的。

你在信号处理程序中真正应该做的只是

  • 设置全局标志
  • 在适当的时候检查代码中的其他位置,并采取措施

或者

  • 有某种主循环,使用select()/ poll()或类似方法监视事件的文件描述符。
  • 在主循环中创建一个管道并监控它
  • 将一个字节写入信号处理程序中的管道
  • 运行代码处理信号,包括在主循环检测到该管道上的事件时保护任何共享数据

或者

  • 保持备用线程
  • 阻止所有线程中的给定信号
  • 在使用信号掩码调用sigsuspend()时有备用线程循环,确保传递该信号。
  • 运行您的代码,包括保护任何共享数据以在sigsuspend()返回时处理信号

答案 1 :(得分:5)

  

是信号处理程序   触发并执行,   未预占?

不,信号处理程序像任何其他用户级功能一样是抢占式的。

  

我认为信号处理程序已执行   在内核中(是吗?)

不,信号处理程序不在内核模式下执行。

内核在从内核模式切换到用户模式时检查进程的挂起信号。如果它找到待处理信号,则设置用户的堆栈帧,使得在返回用户模式之后,该过程开始执行信号处理程序。此后,进程在用户模式下开始执行,执行信号处理程序,就像任何其他用户级别函数一样。执行完成后,进程切换到内核模式。然后内核恢复进程的原始上下文,在信号处理之前执行 所有这种模式切换都不是魔术。内核更改用户堆栈中的相应返回地址。

答案 2 :(得分:2)

简短的回答是“不”。

阅读sigaction,尤其是sa_mask字段。默认情况下,即使在信号处理程序中,您的线程也可能被另一个信号中断。

此外,短语“由进程p中的任何线程中断”没有意义。通常,线程并发运行;它们不会相互“中断”(除非通过调用pthread_kill())。