信号处理程序访问队列数据结构(竞态条件?)

6
我正在用C++编写一个小型shell。
作业及其相关的PID存储在作业指针队列(job*)中。运行新任务时,将其信息添加到队列中。由于可以同时处理多个任务,并且可以随时在shell的控制台输入新任务,因此我有一个信号处理程序来等待终止的作业。
当作业终止时,我需要从活动作业队列中删除其信息并将其移动到已终止作业的双端队列中。但是,当另一个作业停止时,可能正在向队列中添加用户的新作业。
在这种情况下,他们的insert队列操作将被挂起,我的信号处理程序将被调用,它将执行其pop操作。
我正在尝试理解如何解决这种潜在的竞争条件,因为我想象在此过程中可能会发生损坏。我不能使用互斥锁,因为如果被中断的父进程正在使用队列,则会发生死锁。
我看到一些关于C++11能够由用户声明的原子操作的信息,以及有关任务的信息。然而,我不确定这些与我的问题是否相关。

有趣的是,我正在使用作为参考的一个示例 shell(MSH-http://code.google.com/p/mini-shell-msh/),似乎没有处理这种情况。信号处理程序立即修改作业列表以及主控台。也许我在这里忽略了什么?

一如既往,欢迎所有反馈意见。


a) STL::Queue到底是什么? b) 在信号处理程序中要非常小心,如果你修改除了sigatomic_t变量以外的任何东西,那么你就会有未定义的行为。 - Kerrek SB
@chustar,大多数多处理器系统不允许禁用中断。因为我不知道目标环境,所以无法这样做。我想必须有更好的办法。 - BSchlinker
@KerrekSB,我指的是“STL”库中提供的队列库,我猜这实际上是std库。http://www.cplusplus.com/reference/stl/queue/将其称为STL容器。 - BSchlinker
通常情况下,信号处理程序只设置一个标志(上述类型的标志),您的主程序循环将检查该标志。(我实际上不知道裸sigatomic_t的并发更新是否被允许。您是否使用多线程?) - Kerrek SB
@KerrekSB,我只有一个主线程来处理执行。看起来我应该使用多线程,让信号处理程序设置一个标志,然后从辅助线程处理更新。 - BSchlinker
显示剩余6条评论
2个回答

4
您有几种方法可以避免竞争条件。
  • 使用无等待(原子)队列来存储作业指针;
  • 使用任何其他类型的队列,但在非处理程序代码中使用sigprocmask进行保护,并在sigaction调用中使用适当的sa_mask值;
  • 根本不使用信号处理程序,而是使用一些非便携式系统调用,该调用允许以同步方式处理信号:在Linux中,可以使用signalfd,不确定其他平台是否支持。

感谢提到 signalfd - BSchlinker

1
你需要在非处理程序代码的关键部分使用 sigprocmask() 禁用信号。这类似于内核中设备驱动程序在更新与中断处理程序共享的结构时,在驱动程序的用户半部分禁用中断。

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