原子操作在Raspberry Pi Pico RP2040 Cortex M0+双核上到底是如何实现的?

问题描述 投票:0回答:1

我试图找出Pi Pico中使用的ARM Cortex-M0是否支持像

fetch_and_add
compare_and_swap
这样的原子指令,我发现了一个线程解释说Cortex-M0没有这样的指令。相反,为了实现原子性,必须依赖特殊的自旋锁外设和 C/C++ SDK 提供的支持。

在检查SDK中

sync.h
中的具体自旋锁实现时,遇到了几个问题:

spin_lock_blocking
的实现:这里,首先禁用中断,然后调用
spin_lock_unsafe_blocking
,它在锁变量上自旋,然后断言内存栅栏。

问题:

  1. 禁用中断肯定会停止同一核心上的抢占,但是如何确保两个核心不会尝试同时读取/修改/写入锁定变量呢?他们在评论中说等待另一个核心,但是如何呢?在其他一些处理器上,这项工作将由 while 循环内的

    test_set
    指令完成,但这里它仅读取指针值。这样安全吗?

  2. 一旦获得锁,内存屏障的目的是什么?

  3. 我不清楚

    spin_lock_unsafe_blocking
    实现中的评论。 “永不阻塞”是什么意思?

以下代码来自sync.h

#define __builtin_expect(x, y) (x)

__force_inline static void spin_lock_unsafe_blocking(spin_lock_t *lock) {
    // Note: we don't do a WFE or anything because, by convention, these spin locks are VERY SHORTLIVED and **NEVER BLOCK** and run
    // with INTERRUPTS disabled (to ensure that)... therefore, nothing on our core could be       blocking us, so we just **need to wait** on another core
    // anyway, which should be finished soon

    while (__builtin_expect(!*lock, 0));
    __mem_fence_acquire();
}

__force_inline static uint32_t spin_lock_blocking(spin_lock_t *lock) {
    uint32_t save = save_and_disable_interrupts();
    spin_lock_unsafe_blocking(lock);
    return save;
}
c multithreading raspberry-pi multicore raspberry-pi-pico
1个回答
0
投票
  1. 禁用中断肯定会停止同一核心上的抢占,但如何确保两个核心都不会尝试抢占 同时读取/修改/写入锁定变量?

硬件,大概是为了“同时”的相关定义。为了实现远程正确,系统的内存控制器必须提供对

spin_lock_t
类型(可能是整数)对象的读取和写入以原子方式执行(即使在
memory_order_relaxed
意义上也足够了)。对于任何特定硬件来说,这是一个有效的假设并不特别令人惊讶,尽管做出这样的假设一般来说并不安全。

他们在评论中 说等待另一个核心,但是怎么办?

while

循环循环,直到
*lock
非零。该实现似乎期望如果 
*lock
 最初为 0,那么在另一个核心上运行的某些东西很快就会将其设置为非零。这就是“自旋锁”中的“自旋”。然而,所提供的两个函数实际上都没有像我期望的那样修改 
*lock
 的值,以实现 
bona fide 锁。如果没有原子 CAS(又名测试/设置)或类似的东西,你就无法真正正确地做到这一点,但你展示的实现甚至没有尝试。我认为它实际上并没有提供任何锁定。

__builtin_expect()

宏大概是作为一个可以插入原子CAS的配置点。

在其他一些处理器上,这 工作将由 while 循环内的 test_set 指令完成,但是 这里只是读取指针值。这样安全吗?

据我所知,这个函数不提供

真正的锁定行为。所以不行。即使在单核机器上也不行。

    一旦获得锁,内存屏障的目的是什么?
锁定具有内存排序和可见性语义。粗略地说,

    共享变量的读取和写入不会在锁获取或锁释放期间重新排序,并且
  • 在该线程释放给定锁之前发生的任何线程写入共享变量对于随后获取相同锁的任何线程都是可见的。
如果栅栏能够发挥作用,那么这将满足锁获取方的重新排序和可见性要求。即使锁实际上并未锁定,提供这些内存排序保证也比没有好得多。

    我不清楚 spin_lock_unsafe_blocking 实现中的注释。他们所说的“从不”是什么意思 阻止'?
这是“块”一词的奇怪用法。我认为这应该意味着在该函数中运行的线程永远不会被

抢占

© www.soinside.com 2019 - 2024. All rights reserved.