3

这是一个简单的玩具程序,它使用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.

到目前为止我是正确的吗?如果没有,请在您的回答中纠正我。

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

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

4

1 回答 1

3

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

c99 规范中有 2 个相关部分:

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

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

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

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

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

于 2016-11-11T02:35:42.237 回答