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

Exactly which variables need to be sig_atomic_t in the context of signal handling?

这是一个使用 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;
}

我想我知道为什么在这个特定程序中 quit 变量需要 volatile sig_atomic_t

  1. 如果没有volatile,编译器可能会将while (!quit) ;优化为无限循环。它没有找到修改 quit 的循环,因此它假定 quit 始终保持 0.
  2. 更新 quit 或读取 quit 应该在一条机器指令中发生。如果更新或读取 quit 需要多个机器指令,那么如果在更新进行时调用信号处理程序,则信号处理程序中的读取可能会在 quit.[=39 中看到不一致的值=]

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

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

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

Can you provide a list of known scenarios where a variable needs to be defined as sig_atomic_t from C standard perspective?

c99 spec 中有 2 个相关部分:

(§7.14 p2)
[The sig_atomic_t type] is the (possibly volatile-qualified) integer type of an object that can be accessed as an atomic entity, even in the presence of asynchronous interrupts

(§7.14.1.1 p5)
If the signal occurs other than as the result of calling the abort or raise function, the behavior is undefined if the signal handler refers to any object with static storage duration other than by assigning a value to an object declared as volatile sig_atomic_t, ...

"Static storage duration" 定义为:

(§6.2.4 p3)
An object whose identifier is declared with external or internal linkage, or with the storage-class specifier static has static storage duration. Its lifetime is the entire execution of the program and its stored value is initialized only once, prior to program startup.

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