C 挥发性有机污染物及其有害影响
我是一名嵌入式开发人员,在使用I/O端口时使用volatile关键字。但是我的项目经理建议使用volatile关键字是有害的,并且有很多缺点,但是我发现在大多数情况下volatile在嵌入式编程中是有用的,据我所知,volatile在内核代码中是有害的,因为代码的更改将变得不可预测。在嵌入式系统中使用volatile也有缺点吗 Volatile告诉编译器不要优化与Volatile变量有关的任何内容 为什么不应使用“volatile”类型类内核文档中的最佳文章C 挥发性有机污染物及其有害影响,c,linux-kernel,embedded,embedded-linux,C,Linux Kernel,Embedded,Embedded Linux,我是一名嵌入式开发人员,在使用I/O端口时使用volatile关键字。但是我的项目经理建议使用volatile关键字是有害的,并且有很多缺点,但是我发现在大多数情况下volatile在嵌入式编程中是有用的,据我所知,volatile在内核代码中是有害的,因为代码的更改将变得不可预测。在嵌入式系统中使用volatile也有缺点吗 Volatile告诉编译器不要优化与Volatile变量有关的任何内容 为什么不应使用“volatile”类型类内核文档中的最佳文章 volatile是c语言中的一个关键
volatile是c语言中的一个关键字,它告诉编译器不要对该变量进行任何类型的优化
volatile int temp;
for ( i=0 ;i <5 ; i++ )
{
temp = 5;
}
让我举一个简单的例子:
int temp;
for ( i=0 ;i <5 ; i++ )
{
temp = 5;
}
int-temp;
for(i=0;ivolatile
仅在限定对象可以异步更改时才有用。此类更改可能发生
- 如果对象实际上是一个硬件IO寄存器或类似的,并且在程序外部发生了更改
- 如果对象可能被信号处理程序更改
- 如果在调用
setjmp
和longjmp
在所有这些情况下,必须声明对象volatile
,否则程序将无法正常工作。(您可能会注意到,不同线程之间共享的对象不在列表中。)
在所有其他情况下,您都不应该这样做,因为您可能会错过优化机会。另一方面,限定一个不属于上述几点的对象volatile
,不会使您的代码不正确。不,volatile
不会有害。在任何情况下。永远都不会。没有可能的格式良好的代码段在对象中添加volatile
(以及指向该对象的指针)后,这将中断然而,volatile
通常被理解得很差。内核文档声明volatile
被认为是有害的原因是人们一直在用它来中断内核线程之间的同步。特别是,他们使用volatile
整数变量,就好像访问它们是g保证是原子的,但事实并非如此
volatile
也不是无用的,特别是如果你是裸机,你会需要它。但是,像其他任何工具一样,在使用它之前理解volatile
的语义是很重要的
什么是易失性
在本标准中,访问volatile
对象被视为一种副作用,其方式与通过++
和--
递增或递减的方式相同
(…)如果一个实际的实现可以推断出它的值没有被使用,并且没有产生需要的副作用(包括调用函数或访问易失性对象引起的副作用),那么它就不需要对表达式的一部分求值
不适用。编译器必须在每个序列点丢弃它认为知道的关于volatile
变量值的所有信息。(与其他副作用一样,当访问volatile
对象时,由序列点控制)
其效果主要是禁止某些优化
int i;
void foo(void) {
i = 0;
while(i == 0) {
// do stuff that does not touch i
}
}
编译器可以将其放入一个无限循环中,不再检查i
,因为它可以推断i
的值在循环中没有改变,因此i==0
将永远不会为假。即使有另一个线程或中断处理程序可能发生改变,这仍然成立i
。编译器不知道它们,也不关心它们。明确允许它不关心它们
将此与
int volatile i;
void foo(void) {
i = 0;
while(i == 0) { // Note: This is still broken, only a little less so.
// do stuff that does not touch i
}
}
现在,编译器必须假设i
可以随时更改,并且不能进行此优化。当然,这意味着,如果处理中断处理程序和线程,volatile
对象是同步所必需的。但是,它们不够。
什么是易变的
<> > <代码> Value不保证是原子访问。如果您习惯于嵌入编程,这应该是直观的。如果您愿意,考虑一下8位AVR MCU的以下代码:
uint32_t volatile i;
ISR(TIMER0_OVF_vect) {
++i;
}
void some_function_in_the_main_loop(void) {
for(;;) {
do_something_with(i); // This is thoroughly broken.
}
}
此代码被破坏的原因是对i
的访问不是原子性的——在8位MCU上不能是原子性的。例如,在这种简单的情况下,可能会发生以下情况:
isi
0x0000ffff
即将被调用do\u(i)
的两个高位字节被复制到该调用的参数槽中i
- 此时,计时器0溢出,主循环中断
- ISR更改为
。i
的下两个字节溢出,现在是i
0
现在是i
0x00010000
- 主循环继续,将
的下两个字节复制到参数槽中i
do\u使用
作为参数调用带有0
0的东西
uint32_t x;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
x = i;
}
do_something_with(x);
…其中原子块
m
uint32_t x;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
x = i;
}
do_something_with(x);
atomic_int i;
void foo(void) {
atomic_store(&i, 0);
while(atomic_load(&i) == 0) {
// do stuff that does not touch i
}
}