这是一个使用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
变量是必需的。
volatile
,编译器可能会将while (!quit) ;
优化为无限循环。它没有找到修改quit
的循环,因此它假定quit
始终保持0
。quit
或读取quit
。如果需要多个机器指令来更新或读取quit
,那么如果在进行更新时调用信号处理程序,则信号处理程序中的读取可能会在quit
中看到不一致的值。到目前为止我是否正确?如果没有,请在答案中更正我。
现在我想学习在信号处理环境中何时需要sig_atomic_t
的通用规则。 Jonathan Leffler在评论中解释说提供概括并不容易。
您能否提供一个已知场景的列表,其中变量需要从C标准角度定义为sig_atomic_t
?它不一定是一个详尽的清单。它可能是一个经验不足的开发人员在编写带有信号处理代码的C软件时可以参考的列表。
答案 0 :(得分:1)
您能否提供一个已知场景的列表,其中变量需要从C标准角度定义为
sig_atomic_t
?
c99 spec有两个相关部分:
(§7.14p2)
[sig_atomic_t
类型]是一个(可能是volatile限定的)整数类型的对象,即使在存在异步中断的情况下也可以作为原子实体进行访问(§7.14.1.1p5)
如果信号的出现不是调用abort
或raise
函数的结果,那么如果信号处理程序引用具有静态存储持续时间的任何对象,而不是通过为对象赋值,则行为是未定义的声明为volatile sig_atomic_t
,...
“静态存储持续时间”定义为:
(§6.2.4p3)
标识符使用外部或内部链接或使用存储类说明符static
声明的对象具有静态存储持续时间。它的生命周期是程序的整个执行,它的存储值只在程序启动之前初始化一次。
简而言之,如果可以异步访问变量(即,在信号处理程序的内部和外部访问变量),则需要使用volatile sig_atomic_t
。此外,访问具有静态存储持续时间的非volatile sig_atomic_t
变量是未定义的行为。未定义的行为意味着不仅变量的值可能不一致,程序也可以完全执行其他操作(如segfault)。