【问题标题】:Compare and Exchange on Android (ARM)在 Android (ARM) 上进行比较和交换
【发布时间】:2015-01-22 18:20:08
【问题描述】:

下面的代码是android上比较和交换的ARM实现:

__ATOMIC_INLINE__ int __bionic_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t* ptr) {
  int32_t prev, status;
  do {
    __asm__ __volatile__ (
          "ldrex %0, [%3]\n"
          "mov %1, #0\n"
          "teq %0, %4\n"
#ifdef __thumb2__
          "it eq\n"
#endif
          "strexeq %1, %5, [%3]"
          : "=&r" (prev), "=&r" (status), "+m"(*ptr)
          : "r" (ptr), "Ir" (old_value), "r" (new_value)
          : "cc");
  } while (__builtin_expect(status != 0, 0));
  return prev != old_value;
}

即使条件不相等,strexeq 是否会清除 ldrex 中设置的监视器,如果不相等,这如何安全?

还有,为什么我们需要额外的 it eq 用于 thumb2?

【问题讨论】:

  • 16 位 thumb/thumb2 指令没有像 32 位 ARM 指令那样用于谓词执行的编码空间,因此您需要 it eq 来使 thumb2 指令成为有条件的。

标签: android c assembly arm atomic


【解决方案1】:

即使条件不相等,strexeq 是否会清除ldrex 中设置的监视器?

没有。也不需要 - 这是 cmpxchg 的“cmp”部分 - 如果加载的值不是预期的值,则 teq 给出 ne 条件,没有任何反应,我们退出循环由于到mov %1, #0,返回,所有人都忘记了整件事。

如果加载的值正确的,那么我们尝试条件strex来交换它。

所有ldrex 所做的只是设置一个标志(专用监视器)来表示“自从我的ldrex 以来没有人接触过这个内存区域”。如果有人随后写入该区域,则清除该标志。当且仅当它发现标志仍然设置时,strex 才会成功。如果发现flag被清空,说明加载的值可能在内存中发生了变化,违反了操作的原子性,所以存储失败,没有更新发生。在这种情况下,我们必须从头开始并从头开始重试 - 最终,我们将不间断地完成整个序列,此时它似乎是一次原子更新。

在这两种情况下都无需担心独占监视器状态;根据定义,任何以后的独占代码都将以ldrex 开头,这将在那时适当地初始化监视器。

还有,为什么我们需要额外的 it eq 用于 thumb2?

因为 Thumb 没有条件执行(分支除外),因此没有位可以在指令编码中嵌入条件代码。 Thumb-2 引入了the it instruction 作为一种通过全局 ITSTATE 根据特定条件(或其相反条件)生成最多 4 个后续指令块的方法。虽然一些汇编器足够聪明,可以在为 Thumb-2 汇编 ARM 代码时自动生成适当的 it 块,但在可移植代码中您不一定可以依赖它。

在为 ARM 进行汇编时,表现良好的汇编器应该忽略 it(但如果它与以下说明中的条件不匹配,仍然会出错),但为了愚蠢的编译器的利益,它可能在这里被预处理了通过计算换行符来猜测内联 asm 块的长度。

【讨论】:

  • 所以如果我们因为这种情况不调用 strex,我们不需要 clrex 吗?
  • clrex 更多用于当操作系统重新调度并且某些缓存行将被刷新/更新等时。不平衡ldrex 可以,就像许多线程已经启动了例程一样同时。重要的是内存(*ptr)在缓存中; Linux 使用pld 来确保这一点。
  • 我添加了一个额外的段落,其中包含我理解中缺少的部分。希望这有助于下一个人遇到这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-10
  • 2011-05-11
  • 2013-10-27
  • 1970-01-01
相关资源
最近更新 更多