【问题标题】:Is std::atomic<T> safe with interrupts when std::atomic<T>::is_always_lock_free is false?当 std::atomic<T>::is_always_lock_free 为假时,std::atomic<T> 对中断安全吗?
【发布时间】:2018-11-22 01:27:09
【问题描述】:

在没有操作系统的嵌入式 (ARM) 环境中,如果我使用中断,那么使用std::atomic&lt;T&gt; 是否有可能出现死锁?如果是这样,怎么做?

一般来说,任何时候,控制都可以被中断来处理中断。特别是,如果一个人天真地拥有一个互斥体并想用它来对变量进行“安全”操作,则可能会对其进行锁定、写入和解锁,然后在其他地方锁定、读取和解锁。但如果读取处于中断状态,则可以锁定、中断、锁定 => 死锁。

特别是,我有一个std::atomic&lt;int&gt;,其中is_always_lock_freefalse。我应该担心死锁案吗?当我查看生成的程序集时,42 的写法如下:

bl __sync_synchronize
mov r3, #42
str r3, [sp, #4]
bl __sync_synchronize

这似乎没有被锁定。读取值的 asm 类似。像exchange 这样更高级的操作是否(可能)锁定?

【问题讨论】:

  • 如果__sync_synchronize 只是一个内存屏障,那么这段代码只能防止多核重新排序或预取指令。据我所知,它不能防止中断,因此生成的汇编程序不是中断安全的。 __sync_synchronize 必须禁用所有可屏蔽中断才能确保此代码安全。如果不是,我会称实现不合格。
  • 要排除这与之前的许多 C++ 故障一样,您可以尝试 C11 _Atomic int 并查看它是否生成相同的汇编程序。
  • 我相信内存读写在我的处理器上是原子的,而且我相信它不会重新排序,所以我认为至少就我的目的而言,同步不是必需的。
  • 这是几条指令,因此根据定义它不是原子的。

标签: c++ embedded interrupt atomic


【解决方案1】:

__sync_synchronize 只是一个用于完整内存屏障的builtin。不涉及锁定,因此不会像使用互斥锁和中断处理程序那样发生死锁。

您使用的是什么 ARM 内核?在 ARM Cortex-A7 上,两者都打印出true

#include <iostream>
#include <atomic>

int main()
{
   std::atomic<int> x;
   std::cout << std::boolalpha << x.is_lock_free() << std::endl;
   std::cout << std::atomic<int>::is_always_lock_free << std::endl;
}

我希望 std::atomic&lt;int&gt; 在 ARM 上实现大多数(如果不是全部)没有锁,当然从您提供的程序集来看,它似乎没有使用锁。

【讨论】:

  • 我相信它是Coretex M0+。实际变量是std::atomic&lt;int32_t&gt; p;,如果我这样做static_assert(decltype(p)::is_always_lock_free);,我会在编译时点击static_assert。 (程序集示例实际上来自 godbolt.org 使用 ARM gcc 7.2.1(无)。)
  • 很确定使用 gobolt 并不好,因为 'ARM' 涵盖了相当大的范围。您确实需要查看为您的特定芯片生成的内容。但如果它的 Cortex M0,我认为是 ARMv6-M。阅读 infocenter.arm.com/help/topic/com.arm.doc.ddi0419d/… 和 A3.5.1 和“单拷贝原子性”,我看不出有什么理由不总是无锁的。
  • 这是关于嵌入式的。 “主流”是指从 S08 到 x64 的任何内容。后者不太可能用于裸机(除非我们谈论 BIOS)。你不能假设任何事情。几乎所有 8/16 位 MCU 和一些更大的 MCU 也必须对int 操作使用某种锁定方式。其余的可能与重试循环有关,但这可能会更糟,具体取决于项目。
  • @Olaf 在 S08 之类的情况下,它可以在一定程度上执行原子的 int 操作。但是你根本不能相信像int a, b; ... a = b; 这样的C 代码是原子的。您必须禁用中断或编写内联汇编程序才能 100% 确定。
  • @Lundin:那应该是针对 OP 的,而不是针对我的,我认为这基本上是我写的。 (并且,对于 S08,它取决于具体的操作等)。尽管如此,不要依赖编译器会发出这段代码。但这不是重点,S08 只是作为一个小型 MCU 出现的。无论如何,感谢您提供更多详细信息,同时 OP 已经编辑了答案。还是有问题的。 ARM 不会使用锁,但可能需要使用独占加载/存储进行多次迭代,这可能比短中断锁更昂贵。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-27
  • 2021-06-21
  • 1970-01-01
  • 2021-12-28
  • 1970-01-01
  • 2015-03-03
相关资源
最近更新 更多