我正在制作嵌入式固件,其中ISR中初始化后的所有内容。我有他们之间共享的变量,我想知道在哪些情况下他们需要变化。我从不阻止,等待另一个ISR的改变。
何时可以确定在不使用volatile时读取或写入实际内存?每个ISR一次?
附录:
这适用于ARM Cortex-M0,但这并不是关于ISR的问题,而是关于编译器优化的问题,因此,平台不应该非常重要。
答案 0 :(得分:3)
问题完全是可以回答的,答案很简单:
没有volatile你(简单地说)不能假设实际的内存访问将永远发生 - 编译器可以自由地断定结果是完全未使用的(如果它看起来很明显) ),或者它们可以安全地缓存在寄存器中,或者不按顺序计算(只要保持可见依赖性)。
您需要volatile来告诉编译器访问的副作用可能对优化程序无法分析的内容很重要,例如中断上下文或不同“线程”的上下文。
实际上volatile是如何对编译器说的“我知道你不知道的事情,所以不要试图在这里聪明”
请注意,volatile 不保证读取 - 修改 - 写入的原子性,或者甚至在数据类型(或其未对齐)需要多步访问的情况下单独读取或写入。在这些情况下,您不仅要冒一个陈旧的价值,而且要冒一个完全错误的价值。
答案 1 :(得分:1)
已经提到过,当使用非易失性变量时,实际写入内存/缓存并不是完全可预测的。 但是值得一提的是另一个方面,volatile变量可能会被缓存,并且可能需要强制缓存刷新来写入实际内存(取决于是使用直写还是写回策略)。
考虑另一种不缓存volatile变量的情况(放在不可缓存的区域) 但是由于存在写缓冲区和总线桥,有时在实际写入发生在预期寄存器时是不可预测的,并且它需要虚拟读取以确保写入实际发生在实际寄存器/存储器中。这对于避免中断清除/屏蔽中的竞争条件特别有用。
尽管编译器不应该对volatile变量有所了解。对于易失性序列点可以自由地进行一些优化(不允许跨序列点进行优化,但允许在序列点之间进行优化)
答案 2 :(得分:0)
需要volatile
的变量是:
1)在ISR与节目数据或其他线程之间共享数据 优选的是这些标志,指示对各种数据结构的块访问。
// main() code;
disable_interrupts();
if (flag == 0) {
flag = 1;
enable_interrupts();
Manipulate_data();
flag = 0;
} else {
enable_interrupts();
Cope_with_data_unavailable();
}
2)内存映射硬件寄存器
这"记忆"由于硬件条件的原因,编译器可以随时更改,并且编译器需要知道它们的值不一定一致。如果没有volatile
,那么天真的comapiler只会对fred
进行一次采样,从而导致潜在的无限循环。
volatile int *fred = 0x1234; // Hardware reg at address 0x1234;
while (*fred);