信号处理程序中的竞争条件与静态变量

时间:2013-05-29 20:21:02

标签: c signals race-condition signal-handling

在单线程程序中,信号处理程序中是否存在竞争条件?

void signal_handler(...)
{
  static int i = 0;
  i = i + 10 * 10;
}

想象一下,两个非常接近的信号被抛出,如此接近以至于它们同时进入该功能。

我无法找到有关最新Linux操作系统如何处理此信息的信息。我只知道两个信号都得到了正确处理,但我不知道如何处理。竞争条件是否可能?

任何帮助表示感谢,谢谢!

3 个答案:

答案 0 :(得分:1)

单线程意味着只有一个应用程序一次触摸静态是吗?如果有2个应用程序,则有2个静态且没有竞争条件。

如果这是一个中断处理程序并且i + = 100不是原子的(它可能取决于平台/ CPU),那么它会竞争。

答案 1 :(得分:1)

在某种意义上没有竞争条件(两个信号之间)。不同时传送相同信号的多个信号。 除非采取预防措施,否则可以同时传送不同信号编号的多个信号,如torek的回答所述。

每当涉及静态持续时间(或全局变量)的变量时,您的函数可能不再可重入。这通常对信号处理函数本身并不重要。但是,如果它调用一些访问全局或静态数据的其他函数,那么该函数将看到类似于在关键部分中竞争的两个线程的访问模式。也就是说,你的程序正在调用这样一个函数来进行正常处理,但是信号到达该函数的中间,然后你的信号处理程序调用同一个函数。全局/静态变量可能处于不一致状态,并可能导致程序具有非确定性行为。

POSIX定义了一组可以安全地从信号处理程序中调用的API。当您计划让信号处理程序调用您实现的函数时,您的代码应采取类似的预防措施。

答案 2 :(得分:1)

另外一个重要的注意事项:如果您使用“可靠信号”(POSIX sigaction和相应的sa_mask字段),您可以控制信号在单线程中的行为方式 - 处理情况。

考虑单个进程P1的情况,使用与上面显示的信号处理程序类似的信号处理程序。假设你正在捕捉信号SIGUSR1并让它进入函数signal_handler。当您在signal_handler内时,其他一些流程P2会向P1发送另一个SIGUSR1(例如,通过kill)。此信号通过sa_mask“暂时”阻止(暂时),直到signal_handler在P1中返回。即使您未在sa_mask中设置任何位,也是如此(只要您未在SA_NODEFER中设置sa_flags,请参见下文。)

但是,假设您还决定使用函数SIGUSR2捕获signal_handler。假设P2也发送SIGUSR2。在这种情况下,SIGUSR2已被(或可能)被捕获,此时代表signal_handler信号开始另一个SIGUSR2运行实例。

您可以通过确保在处理SIGUSR1时暂时阻止SIGUSR2来阻止此操作。一般情况下,您可能希望在处理SIGUSR1时阻止SIGUSR2。为此,请在sa_mask

中设置相应的位
struct sigaction sa;

memset(&sa, 0, sizeof sa);
sa.sa_flags = SA_RESTART | SA_SIGINFO; /* (decide for yourself which flags) */
sigaddset(&sa.sa_mask, SIGUSR1);
sigaddset(&sa.sa_mask, SIGUSR2);
sa.sa_sigaction = signal_handler;
error = sigaction(SIGUSR1, &sa, NULL);
if (error) ... handle error ...
error = sigaction(SIGUSR2, &sa, NULL);
if (error) ... handle error ...

两个sigaddset调用确保SIGUSR1和SIGUSR2在函数持续期间保持关闭(暂时阻止)。

如果您只捕获一个信号,则不需要这种额外的复杂性,因为只要SA_NODEFER 设置,操作系统就会自动添加任何触发输入的信号你的信号处理程序在入口处设置“当前被阻止的信号”。

(请注意,使用sigprocmaskSIG_BLOCK而不是{{1} - 通过保存通过SIG_SETMASK填充的前一个掩码设置退出时SIG_UNBLOCK的掩码。好吧,它通常在内核代码中完成,而不是实际调用SIG_SETMASK,但效果是一样的,效率更高。)