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

Ric*_*ard 6 multithreading operating-system signal-handling

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

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

Lyk*_*yke 16

几乎 - 不要 - 在信号处理程序中处理共享数据几乎总会导致一个痛苦的世界,同时处理线程,你自己也搞得一团糟.

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

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

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

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

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

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

  • 设置全局标志
  • 在适当的时候检查代码中的其他地方的标志,并采取行动

要么

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

要么

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


Ter*_*nal 5

信号处理程序在被触发和执行时是否是不可抢占的?

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

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

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

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