- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我是一名嵌入式开发人员,在使用 I/O 端口时使用 volatile 关键字。但是我的项目经理建议使用 volatile 关键字是有害的并且有很多缺点,但是我发现在大多数情况下 volatile 在嵌入式编程中很有用,据我所知 volatile 在内核代码中是有害的,因为对我们的代码的更改将变得不可预测的。在嵌入式系统中使用 volatile 也有什么缺点吗?
最佳答案
不,volatile
无害。在任何情况下。曾经。添加 volatile
后,没有可能的格式良好的代码会中断。到一个对象(以及指向该对象的指针)。然而, volatile
往往知之甚少 .内核文档说明 volatile
的原因被认为是有害的,因为人们一直使用它以破坏的方式在内核线程之间进行同步。特别是,他们使用了 volatile
整数变量就好像对它们的访问保证是原子的,但事实并非如此。volatile
也不是没用,特别是如果你去裸机,你会需要它。但是,与任何其他工具一样,理解 volatile
的语义很重要。在使用它之前。
什么 volatile
是
访问 volatile
在标准中,对象被认为是副作用,就像增加或减少 ++
一样。和 --
.特别是,这意味着 5.1.2.3 (3),它说
(...) An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced (including any caused by calling a function or accessing a volatile object)
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
对象是同步所必需的。
然而,它们还不够。
volatile
不是
volatile
不保证是原子访问。如果您习惯于嵌入式编程,这应该很直观。如果您愿意,请考虑以下用于 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 上不能是原子的。例如,在这个简单的情况下,可能会发生以下情况:
i
是 0x0000ffff
do_something_with(i)
即将被叫i
的高两个字节被复制到此调用的参数槽中 i
. i
的低两个字节溢出,现在是 0
. i
现在是 0x00010000
. i
的低两个字节被复制到参数槽 do_something_with
用 0
调用作为其参数。 volatile
还不错,您将(通常)必须在裸机代码中执行此操作。然而,当你使用它时,你必须记住它不是魔杖,你仍然需要确保你不会被自己绊倒。在嵌入式代码中,通常有一种特定于平台的方法来处理原子性问题;例如,在 AVR 的情况下,通常的 Crowbar 方法是在持续时间内禁用中断,如
uint32_t x;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
x = i;
}
do_something_with(x);
ATOMIC_BLOCK
宏调用
cli()
(禁用中断)之前和
sei()
(启用中断)之后,如果它们事先被启用。
volatile
不必要。如果您可以使用它们,请使用它们,但它们可能需要一段时间才能到达所有常见的嵌入式工具链。有了它们,上面的循环可以像这样修复:
atomic_int i;
void foo(void) {
atomic_store(&i, 0);
while(atomic_load(&i) == 0) {
// do stuff that does not touch i
}
}
关于c - volatile 及其有害影响,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27777140/
这将是一篇很长的文章,为了将其上下文化并提供尽可能多的信息,我必须仔细浏览各种链接和引号——这通常是我们进入 C/C++ 标准兔子洞的唯一方法。如果您对这篇文章有更好的引用或任何其他改进,请告诉我。但
我想知道 volatile 关键字与 register、const 和 static 结合的不同用途关键词。我不确定有什么影响,所以我认为: register volatile int T=10; 建
让我们考虑以下 Java 代码 int x = 0; int who = 1 Thread #1: (1) x++; (2) who = 2; Thread #2 while(who
有一个函数“remove_cv”(http://en.cppreference.com/w/cpp/types/remove_cv)可以删除常量和 volatile 。 我的问题是为什么可以从“con
我正在尝试在下面的“MpscQueue.h”中的嵌入式目标上实现多个生产者(通过中断)、单个消费者(通过应用程序线程)队列。 我想知道我是否可以安全地删除一些 volatile下面的用法(见内联问
我的问题适用于最初为 null 的字段,然后初始化为非 null 值,然后不再更改。 由于该字段需要尽快可供所有线程使用,因此我需要使用 volatile 。 但是,如果我想尽可能避免 volatil
我以前见过几次类似 fld = fld 的东西,但在所有这些情况下,可以消除虚拟写入并获得更好的性能。 public class Tst{ public volatile int fld =
看完this question和 this (尤其是第二个答案)我对 volatile 及其与内存屏障有关的语义感到非常困惑。 在上面的例子中,我们写入了一个 volatile 变量,这会导致一个 m
如下所示,该程序有一个共享 var flag,但不带 volatile : public class T { public static void main(String[] args) {
我明白声明 int *volatile ptr; 表示指针本身是volatile int a=10; int *volatile ptr=&a; 现在 ptr 和 a 都在更新。会不会导致访问ptr时
最近我需要比较两个 uint 数组(一个是 volatile 数组,另一个是非 volatile 数组),结果令人困惑,我一定是对 volatile 数组有一些误解。 我需要从输入设备读取一个数组并将
这两个 C 定义有什么区别? volatile uint32_t *ptr1 = (volatile uint32_t *)0x20040000; volatile uint32_t *ptr1 =
// structure is like this, but not exact formation. class queue { volatile List worksWaiting; }
考虑以下这段代码: struct S{ int i; S(int); S(const volatile S&); }; struct S_bad{ int i; }; vola
在 Windows x64 上,考虑到一些额外的见解,何时允许编译器将 ABI 标记为 volatile 的寄存器视为非 volatile 寄存器?我有一个反汇编函数,其中 r11 用于在函数调用后恢
我对下面的代码段有疑问。结果可能有 [0, 1, 0] 的结果(这是用 JCStress 执行的测试)。那么这怎么会发生呢?我认为应该在写入 Actor2 (guard2 = 1) 中的 guard2
好吧,假设我有一堆变量,其中一个声明为 volatile: int a; int b; int c; volatile int v; 如果一个线程写入所有四个变量(最后写入 v),而另一个线程读取所有
我试图理解为什么这个例子是一个正确同步的程序: a - volatile Thread1: x=a Thread2: a=5 因为存在冲突访问(存在对 a 的写入和读取),所以在每个顺序一致性执行中,
我正在编写一个需要同时支持 volatile 和非 volatile 实例的类( volatile 实例使用原子操作,非 volatile 实例使用常规操作),并且想知道我是否以正确的方式进行处理。到
我正在为 Cortex-M0 CPU 和 gcc 编写代码。我有以下结构: struct { volatile unsigned flag1: 1; unsigned flag2: 1
我是一名优秀的程序员,十分优秀!