在信号处理的上下文中,究竟哪些变量需要是sig_atomic_t?
这是一个使用volatile sig_atomic_t
的简单玩具程序。
#include #include #include #include #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软件时可以参考的列表。
您能否提供一个已知场景的列表,其中变量需要从C标准角度定义为
sig_atomic_t
?
c99规范中有2个相关部分:
(§7.14p2)
[sig_atomic_t
类型]是可以作为primefaces实体访问的对象的(可能是volatile限定的)整数类型,即使存在异步中断也是如此(§7.14.1.1p5)
如果信号不是作为调用abort
或raise
函数的结果发生的,那么如果信号处理程序引用具有静态存储持续时间的任何对象,而不是通过将值volatile sig_atomic_t
声明为volatile sig_atomic_t
的对象,则行为是未定义的,…
“静态存储持续时间”定义为:
(§6.2.4p3)
标识符使用外部或内部链接声明的对象,或者使用存储类说明符static
声明的对象具有静态存储持续时间 。 它的生命周期是程序的整个执行,它的存储值只在程序启动之前初始化一次。
简而言之,如果可以异步访问变量(即,在信号处理程序的内部和外部访问变量),则需要使用volatile sig_atomic_t
。 此外,访问具有静态存储持续时间的非易失volatile sig_atomic_t
变量是未定义的行为。 未定义的行为意味着不仅变量的值可能不一致,程序也可以完全执行其他操作(如segfault)。