在信号处理的上下文中,究竟哪些变量需要是sig_atomic_t?

时间:2016-11-10 02:01:03

标签: c signals sig-atomic-t

这是一个使用volatile sig_atomic_t的简单玩具程序。

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>

#define UNUSED(x) (void) (x)

volatile sig_atomic_t quit;

void sigusr1_handler(int sig)
{
    UNUSED(sig);
    write(1, "handler\n", 8);
    quit = 1;
}

int main()
{
    struct sigaction sa;

    sa.sa_handler = sigusr1_handler;
    sa.sa_flags = 0;
    sigemptyset(&sa.sa_mask);

    if (sigaction(SIGUSR1, &sa, NULL) == -1) {
        perror("sigaction");
        return 1;
    }

    quit = 0;
    while (!quit)
        ;

    printf("Exiting ...\n");
    return 0;
}

我想我知道为什么volatile sig_atomic_t对于此特定程序中的quit变量是必需的。

  1. 如果没有volatile,编译器可能会将while (!quit) ;优化为无限循环。它没有找到修改quit的循环,因此它假定quit始终保持0
  2. 单个机器指令中应更新quit或读取quit。如果需要多个机器指令来更新或读取quit,那么如果在进行更新时调用信号处理程序,则信号处理程序中的读取可能会在quit中看到不一致的值。
  3. 到目前为止我是否正确?如果没有,请在答案中更正我。

    现在我想学习在信号处理环境中何时需要sig_atomic_t的通用规则。 Jonathan Leffler在评论中解释说提供概括并不容易。

    您能否提供一个已知场景的列表,其中变量需要从C标准角度定义为sig_atomic_t?它不一定是一个详尽的清单。它可能是一个经验不足的开发人员在编写带有信号处理代码的C软件时可以参考的列表。

1 个答案:

答案 0 :(得分:1)

  

您能否提供一个已知场景的列表,其中变量需要从C标准角度定义为sig_atomic_t

c99 spec有两个相关部分:

  

(§7.14p2)
  [sig_atomic_t类型]是一个(可能是volatile限定的)整数类型的对象,即使在存在异步中断的情况下也可以作为原子实体进行访问

     

(§7.14.1.1p5)
  如果信号的出现不是调用abortraise函数的结果,那么如果信号处理程序引用具有静态存储持续时间的任何对象,而不是通过为对象赋值,则行为是未定义的声明为volatile sig_atomic_t,...

“静态存储持续时间”定义为:

  

(§6.2.4p3)
  标识符使用外部或内部链接或使用存储类说明符static声明的对象具有静态存储持续时间。它的生命周期是程序的整个执行,它的存储值只在程序启动之前初始化一次。

简而言之,如果可以异步访问变量(即,在信号处理程序的内部和外部访问变量),则需要使用volatile sig_atomic_t。此外,访问具有静态存储持续时间的非volatile sig_atomic_t变量是未定义的行为。未定义的行为意味着不仅变量的值可能不一致,程序也可以完全执行其他操作(如segfault)。