我正在为 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 :
- Memory location
Either an object of scalar type, or a maximal sequence of adjacent bit-fields all having nonzero widthNOTE 1 Two threads of execution can update and access separate memory locations without interfering with each other.
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 char
或 bool
更简单> 对他们来说:
struct {
volatile bool flag1;
bool flag2;
unsigned foo; // something else accessed in main loop
} flags;
现在它们将被放置在不同的内存位置,并且可以在不相互干扰的情况下更新它们。
但是 flag1
的 volatile
仍然是绝对必要的,因为否则更新 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/