2011-05-25 77 views
6

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

PS:我认为信号处理程序在内核中执行(是吗?),并且内核对用户模式线程不具有任何意义?纠正我,如果它是错误的...

+0

它可以被预先占用 - 信号处理程序代码没有特别特别的地方。 – 2011-05-25 18:10:21

+1

内核是先发制人的。它总是有一些事情要做。 – 2011-05-25 18:38:01

回答

16

非常多 - 不要 - 处理信号处理程序中的共享数据几乎总是会导致一个痛苦的世界,处理线程以及你自己一团糟。

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

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

但是,任何其他线程禁止处理信号的线程可能并行运行。处理信号的线程可以在程序中的任何点运行信号处理程序(只要信号没有被阻塞)。因此,您需要某种锁定来保护数据。问题是你不能使用任何正常的线程锁定原语,它们不是信号异步安全的。如果你的意思是尝试在信号处理程序中抓取pthread_mutex_t,您很容易使程序死锁。

您可以安全地在信号处理程序中调用的唯一功能是列出的here。 关于保护共享数据,您可以使用sigblock()/ sigunblock()作为一种保护措施,确保信号处理程序在您访问共享数据时不会运行 - 并且信号必须全部阻止线程,否则它只会在没有被阻塞的线程之一内运行 - 走下去的道路是疯狂的。

几乎所有可以在信号处理程序中安全访问的唯一共享数据是sig_atomic_t类型,实际上其他类型的基本类型通常也是安全的。

你真的应该在信号处理程序做的仅仅是

  • 设置一个全局标志
  • 检查该标志在其他地方的代码时适宜,并采取行动

或者

  • 有一些使用select()/ poll()或类似方法监视事件的文件描述符的主循环。
  • 创建管道和监视在主回路
  • 写()到管道中的字节中的信号处理程序
  • 运行代码来处理信号,包括保护任何共享数据时,主循环检测事件对管

或者

  • 保持周围
  • 空闲线程块给定信号中所有的线程
  • 在调用sigsuspend()时有一个备用线程循环,信号掩码确保提供该信号。
  • 运行你的代码,包括保护任何共享的数据来处理信号时,sigsuspend()返回
+0

优秀的解释!您已经涵盖了使用信号以及多线程应用程序的大多数缺陷以及可能的解决方案!感谢您的详细解释。 – yogishaj 2014-08-28 19:27:59

2

简短的答案是“否”。

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

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

5

是信号处理,被 触发和执行, 非抢占什么时候?

不,信号处理程序像任何其他用户级功能一样具有抢占性。

我以为信号处理程序运行于内核 (是吗?)

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

内核在从内核模式切换到用户模式时检查进程的未决信号。如果它发现一个未决信号,它会设置用户的堆栈帧,以便在返回到用户模式后,该进程开始执行信号处理程序。此后进程开始在用户模式下执行,执行信号处理程序,就像任何其他用户级别的函数一样。当执行完成时,进程切换到内核模式。然后内核恢复进程的原始上下文,在信号处理之前执行。
所有这种模式切换并不神奇。内核更改用户堆栈中的相应返回地址。