同步 ISR 访问是否需要 volatile?
Is volatile required for synchronous ISR access?
在以下代码片段中,中断例程使用多个数组中的一个来执行。使用的数组是同步选择的,不是异步选择的(它在 ISR 执行时永远不会改变)。在单核微控制器上(如果架构很重要,这个问题假定是 STM32L496),foo
的声明中是否需要 volatile
说明符?
int a[] = {1, 2, 3};
int b[] = {4, 5, 6};
int * foo; //int * volatile foo? int volatile * volatile foo?
main(){
disable_interrupt();
foo = a;
enable_interrupt();
...
disable_interrupt();
foo = b;
enable_interrupt();
}
void interrupt(){
//Use foo
}
我的假设是 volatile
说明符 不需要 因为 foo
值的任何缓存都是正确的。
编辑:
澄清一下,最终答案是 volatile
或其他一些同步是必需的,因为否则写入 foo
可以省略或重新排序。缓存不是唯一的问题。
volatile
停止编译器优化它,强制编译器
- 始终读取内存,而不是寄存器中的缓存值
- 在 volatile 之前或之后不要移动东西 read/write
在复杂的 CPU(例如 x86)上,CPU 可以在可变访问之前或之后重新排序操作。
它通常用于内存映射 io,其中内存区域实际上是设备,并且可以更改(即使在单个内核 CPU 上),没有明显的原因。
C++11
的机制是使用std::atomic改变一个可能出现在不同执行线程上的值。
对于单核,代码将安全地修改值并存储它。如果您使用 volatile,那么它将在启用中断之前写入内存点。
如果您不使用 volatile,那么在中断中使用新值之前,代码可能仍将新值保存在寄存器中。
int * volatile foo;
描述 foo 可以改变,但它指向的值是稳定的。
int volatile * volatile foo
描述foo是可以改变的,它指向的东西也是可以改变的。我想你想要 int * volatile foo;
更新
对于那些怀疑 volatile
是编译器障碍的人。
来自标准 n4296
Accessing an object designated by a volatile glvalue (3.10), modifying an object, calling a library I/O
function, or calling a function that does any of those operations are all side effects, which are changes in the
state of the execution environment. Evaluation of an expression (or a sub-expression) in general includes
both value computations (including determining the identity of an object for glvalue evaluation and fetching
a value previously assigned to an object for prvalue evaluation) and initiation of side effects. When a call
to a library I/O function returns or an access to a volatile object is evaluated the side effect is considered
complete, even though some external actions implied by the call (such as the I/O itself) or by the volatile
access may not have completed yet.
和
volatile object - an object whose type is volatile-qualified, or a subobject of a volatile object, or a mutable subobject of a const-volatile object. Every access (read or write operation, member function call, etc.) made through a glvalue expression of volatile-qualified type is treated as a visible side-effect for the purposes of optimization (that is, within a single thread of execution, volatile accesses cannot be optimized out or reordered with another visible side effect that is sequenced-before or sequenced-after the volatile access. This makes volatile objects suitable for communication with a signal handler, but not with another thread of execution, see std::memory_order). Any attempt to refer to a volatile object through a non-volatile glvalue (e.g. through a reference or pointer to non-volatile type) results in undefined behavior.
这些似乎一致,存在编译器障碍,但与 volatile 对象交互的一些副作用可能尚未完成。对于单核处理器,如果 C++11 原子不可用,它似乎是一种合适的机制。
我们有:-
Every value computation and side effect associated with a full-expression is sequenced before every value
computation and side effect associated with the next full-expression to be evaluated.
据我了解,任何具有副作用的操作都存在 happens-before
关系。
Access to volatile objects are evaluated strictly according to the rules of the abstract machine
由此我了解到,有规则(可能不透明)。
Accessing an object designated by a volatile glvalue (3.10), modifying an object, calling a library I/O
function, or calling a function that does any of those operations are all side effects, which are changes in the
state of the execution environment. Evaluation of an expression (or a sub-expression) in general includes
both value computations (including determining the identity of an object for glvalue evaluation and fetching
a value previously assigned to an object for prvalue evaluation) and initiation of side effects. When a call
to a library I/O function returns or an access to a volatile object is evaluated the side effect is considered
complete, even though some external actions implied by the call (such as the I/O itself) or by the volatile
access may not have completed yet.
据我所知,访问 volatile(以及其他一些东西)会产生副作用,这会阻止编译器对 volatile 访问附近的语句重新排序。
在以下代码片段中,中断例程使用多个数组中的一个来执行。使用的数组是同步选择的,不是异步选择的(它在 ISR 执行时永远不会改变)。在单核微控制器上(如果架构很重要,这个问题假定是 STM32L496),foo
的声明中是否需要 volatile
说明符?
int a[] = {1, 2, 3};
int b[] = {4, 5, 6};
int * foo; //int * volatile foo? int volatile * volatile foo?
main(){
disable_interrupt();
foo = a;
enable_interrupt();
...
disable_interrupt();
foo = b;
enable_interrupt();
}
void interrupt(){
//Use foo
}
我的假设是 volatile
说明符 不需要 因为 foo
值的任何缓存都是正确的。
编辑:
澄清一下,最终答案是 volatile
或其他一些同步是必需的,因为否则写入 foo
可以省略或重新排序。缓存不是唯一的问题。
volatile
停止编译器优化它,强制编译器
- 始终读取内存,而不是寄存器中的缓存值
- 在 volatile 之前或之后不要移动东西 read/write
在复杂的 CPU(例如 x86)上,CPU 可以在可变访问之前或之后重新排序操作。
它通常用于内存映射 io,其中内存区域实际上是设备,并且可以更改(即使在单个内核 CPU 上),没有明显的原因。
C++11
的机制是使用std::atomic改变一个可能出现在不同执行线程上的值。
对于单核,代码将安全地修改值并存储它。如果您使用 volatile,那么它将在启用中断之前写入内存点。
如果您不使用 volatile,那么在中断中使用新值之前,代码可能仍将新值保存在寄存器中。
int * volatile foo;
描述 foo 可以改变,但它指向的值是稳定的。
int volatile * volatile foo
描述foo是可以改变的,它指向的东西也是可以改变的。我想你想要 int * volatile foo;
更新
对于那些怀疑 volatile
是编译器障碍的人。
来自标准 n4296
Accessing an object designated by a volatile glvalue (3.10), modifying an object, calling a library I/O function, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment. Evaluation of an expression (or a sub-expression) in general includes both value computations (including determining the identity of an object for glvalue evaluation and fetching a value previously assigned to an object for prvalue evaluation) and initiation of side effects. When a call to a library I/O function returns or an access to a volatile object is evaluated the side effect is considered complete, even though some external actions implied by the call (such as the I/O itself) or by the volatile access may not have completed yet.
和
volatile object - an object whose type is volatile-qualified, or a subobject of a volatile object, or a mutable subobject of a const-volatile object. Every access (read or write operation, member function call, etc.) made through a glvalue expression of volatile-qualified type is treated as a visible side-effect for the purposes of optimization (that is, within a single thread of execution, volatile accesses cannot be optimized out or reordered with another visible side effect that is sequenced-before or sequenced-after the volatile access. This makes volatile objects suitable for communication with a signal handler, but not with another thread of execution, see std::memory_order). Any attempt to refer to a volatile object through a non-volatile glvalue (e.g. through a reference or pointer to non-volatile type) results in undefined behavior.
这些似乎一致,存在编译器障碍,但与 volatile 对象交互的一些副作用可能尚未完成。对于单核处理器,如果 C++11 原子不可用,它似乎是一种合适的机制。
我们有:-
Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated.
据我了解,任何具有副作用的操作都存在 happens-before
关系。
Access to volatile objects are evaluated strictly according to the rules of the abstract machine
由此我了解到,有规则(可能不透明)。
Accessing an object designated by a volatile glvalue (3.10), modifying an object, calling a library I/O function, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment. Evaluation of an expression (or a sub-expression) in general includes both value computations (including determining the identity of an object for glvalue evaluation and fetching a value previously assigned to an object for prvalue evaluation) and initiation of side effects. When a call to a library I/O function returns or an access to a volatile object is evaluated the side effect is considered complete, even though some external actions implied by the call (such as the I/O itself) or by the volatile access may not have completed yet.
据我所知,访问 volatile(以及其他一些东西)会产生副作用,这会阻止编译器对 volatile 访问附近的语句重新排序。