gpt4 book ai didi

c++ - 在现代 C++ 中,是否仍应使用 volatile 与 ISR 共享数据?

转载 作者:行者123 更新时间:2023-12-01 11:55:47 25 4
gpt4 key购买 nike

我已经看到了这些问题的一些风格,也看到了不同的答案,但仍然不确定它们是否是最新的并完全适用于我的用例,所以我会在这里提问。如果它是重复的,请告诉我!
鉴于我正在使用 C++17 和 gcc-arm-none-eabi-9 工具链为 STM32 微 Controller (裸机)开发:
我还需要使用volatile吗?用于在 ISR 和 main() 之间共享数据?

volatile std::int32_t flag = 0;

extern "C" void ISR()
{
flag = 1;
}

int main()
{
while (!flag) { ... }
}
我很清楚我应该总是使用 volatile用于访问内存映射的硬件寄存器。
但是对于 ISR 用例,我不知道它是否可以被视为“多线程”的情况。在这种情况下,人们建议使用 C++11 的新线程特性(例如 std::atomic)。我知道 volatile 之间的区别(不优化)和 atomic (安全访问),所以答案提示 std::atomic把我弄糊涂了。
对于 x86 系统上“真正的”多线程的情况,我没有看到需要使用 volatile .
换句话说:编译器可以知道 flag可以在 ISR 内部更改吗?如果没有,它如何在常规多线程应用程序中知道它?
谢谢!

最佳答案

我认为在这种情况下, volatile 和 atomic 最有可能在 32 位 ARM 上实际工作。至少在旧版本的 STM32 工具中,我看到实际上 C 原子是使用 volatile 实现的小类型。
Volatile 会起作用,因为编译器可能不会优化掉对代码中出现的变量的任何访问。
但是,对于不能在单个指令中加载的类型,生成的代码必须不同。如果您使用 volatile int64_t ,编译器会很高兴地在两个单独的指令中加载它。如果 ISR 在加载变量的两半之间运行,您将加载旧值的一半和新值的一半。
不幸的是使用 atomic<int64_t>如果实现不是无锁的,也可能会因中断服务例程而失败。对于 Cortex-M,64 位访问不一定是无锁的,所以不检查实现不应该依赖原子。根据实现,如果锁定机制不可重入并且在持有锁定时发生中断,则系统可能会死锁。从 C++17 开始,可以通过检查 atomic<T>::is_always_lock_free 来查询。 .可以通过检查 flagA.is_lock_free() 获得特定原子变量的特定答案(这可能取决于对齐方式)。从 C++11 开始。
因此,较长的数据必须由单独的机制保护(例如,通过关闭访问周围的中断并使变量成为原子或 volatile 。
所以正确的方法是使用std::atomic ,只要访问是无锁的。如果您关心性能,那么选择合适的内存顺序并坚持可以在单个指令中加载的值可能会有所返回。
不使用两者都是错误的,compiler将只检查一次标志。
这些函数都在等待一个标志,但它们的翻译方式不同:

#include <atomic>
#include <cstdint>

using FlagT = std::int32_t;

volatile FlagT flag = 0;
void waitV()
{
while (!flag) {}
}

std::atomic<FlagT> flagA;
void waitA()
{
while(!flagA) {}
}

void waitRelaxed()
{
while(!flagA.load(std::memory_order_relaxed)) {}
}

FlagT wrongFlag;
void waitWrong()
{
while(!wrongFlag) {}
}
使用 volatile 您会得到一个循环,可以根据需要重新检查标志:
waitV():
ldr r2, .L5
.L2:
ldr r3, [r2]
cmp r3, #0
beq .L2
bx lr
.L5:
.word .LANCHOR0
具有默认顺序一致访问的原子产生同步访问:
waitA():
push {r4, lr}
.L8:
bl __sync_synchronize
ldr r3, .L11
ldr r4, [r3, #4]
bl __sync_synchronize
cmp r4, #0
beq .L8
pop {r4}
pop {r0}
bx r0
.L11:
.word .LANCHOR0
如果你不关心内存顺序,你会得到一个工作循环,就像 volatile 一样:
waitRelaxed():
ldr r2, .L17
.L14:
ldr r3, [r2, #4]
cmp r3, #0
beq .L14
bx lr
.L17:
.word .LANCHOR0
使用 volatile 和 atomic 都不会启用优化,因为该标志只检查一次:
waitWrong():
ldr r3, .L24
ldr r3, [r3, #8]
cmp r3, #0
bne .L23
.L22: // infinite loop!
b .L22
.L23:
bx lr
.L24:
.word .LANCHOR0
flag:
flagA:
wrongFlag:

关于c++ - 在现代 C++ 中,是否仍应使用 volatile 与 ISR 共享数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63471387/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com