c - volatile 和非 volatile 位域

标签 c arm volatile bit-fields

我正在为 Cortex-M0 CPU 和 gcc 编写代码。我有以下结构:

struct {
    volatile unsigned flag1: 1;
    unsigned flag2: 1;

    unsigned foo; // something else accessed in main loop
} flags;

flag1 从 GPIO 中断处理程序和主循环中读取和写入。 flag2 仅在主循环中读写。

ISR 看起来像这样:

void handleIRQ(void) {
    if (!flags.flag1) {
        flags.flag1 = 1;
        // enable some hw timer
    }
}

主循环是这样的:

for (;;) {
    // disable IRQ
    if (flags.flag1) {
        // handle IRQ
        flags.flag1 = 0;
        // access (rw) flag2 many times
    }
    // wait for interrupt, enable IRQ
}

在主循环中访问 flag2 时,编译器是否会优化对它的访问,这样它就不会在每次用代码读取或写入时都被提取或存储到内存中?

我不清楚,因为要在 ISR 中设置 flag1,它需要加载整个 char,设置一个位并将其存储回去。

最佳答案

根据我对 C11 标准的解读,为此使用位域是不合适的 - 即使它们都被声明为 volatile。以下摘自3.14 Memory location :

  1. Memory location
    Either an object of scalar type, or a maximal sequence of adjacent bit-fields all having nonzero width
  2. NOTE 1 Two threads of execution can update and access separate memory locations without interfering with each other.

  3. NOTE 2 It is not safe to concurrently update two non-atomic bit-fields in the same structure if all members declared between them are also (non-zero-length) bit-fields, no matter what the sizes of those intervening bit-fields happen to be.

volatile 没有异常(exception)。因此,如果两个执行线程(即主线程和 ISR)如果 ISR 将更新一个标志而主线程将更新另一个标志,则使用上述位域是不安全的。给出的解决方案是在两者之间添加一个大小为0的成员,以强制将它们放在不同的内存位置。但话又说回来,这意味着两个标志都会消耗至少一个字节的内存,因此再次使用非位域 unsigned charbool 更简单> 对他们来说:

struct {
    volatile bool flag1;
    bool flag2;

    unsigned foo; // something else accessed in main loop
} flags;

现在它们将被放置在不同的内存位置,并且可以在不相互干扰的情况下更新它们。


但是 flag1volatile 仍然是绝对必要的,因为否则更新 flag1没有副作用 在主线程中,编译器可以推断它可以将该字段仅保留在寄存器中 - 或者根本不需要更新任何内容。

但是,需要注意的是,在 C11 下,即使 volatile 的保证也可能不够:5.1.2.3p5 :

When the processing of the abstract machine is interrupted by receipt of a signal, the values of objects that are neither lock-free atomic objects nor of type volatile sig_atomic_t are unspecified, as is the state of the floating-point environment. The value of any object modified by the handler that is neither a lock-free atomic object nor of type volatile sig_atomic_t becomes indeterminate when the handler exits, as does the state of the floating-point environment if it is modified by the handler and not restored to its original state.

因此,如果需要完全兼容,flag1 应该是 volatile _Atomic bool 类型的例子;甚至可以使用 _Atomic 位域。但是,这两者都需要 C11 编译器。

然后,您可以查看编译器的手册,看它们是否保证对此类 volatile 对象的访问也保证是原子的。

关于c - volatile 和非 volatile 位域,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42902622/

相关文章:

c - 在 C 中为光盘驱动器编号,就像 drutil 在 OS X 命令行中所做的那样

c - 套接字如何在 C 中工作?

arm - MIPS和ARM的区别

由中断服务例程切换的 volatile 变量的更改未反射(reflect)在 main() 中

c - C 中的泛型函数指针

c - 在开发模式为 DEBUG 或 RELEASE 后打开/关闭日志记录(例如 zlog)

带有 freertos 的 gnu arm cortex m4 上的 C++ 异常处理程序

c++ - glUniform1ui 的成本

java - volatile 同步组合以提高性能

postgresql - 为什么 PostgreSQL 在从稳定函数调用 volatile 函数时不给出警告?