ARM命令LDREX和STREX实现spinlock

在 include/asm-arm/spinlock.h 下有這麼一段

#if __LINUX_ARM_ARCH__ < 6
#error SMP not supported on pre-ARMv6 CPUs
#endif

好啦,前提就是:只有 ARM core 版本 >=6 才可以繼續

all spin lock primitives 到最後都是使用下面這個基本型: 

static inline void __raw_spin_lock(raw_spinlock_t *lock)
{
    unsigned long tmp;

1      __asm__ __volatile__(
2"1:    ldrex    %0, [%1]\n"
3"    teq    %0, #0\n"
4"    strexeq    %0, %2, [%1]\n"
5"    teqeq    %0, #0\n"
6"    bne    1b"
7   : "=&r" (tmp)
8  : "r" (&lock->lock), "r" (1)
9 : "cc");

    smp_mb();
}

[指令重點]:

ldrex 指令是 core 6 以後才有的,跟 strex 配成一對指令,可以請 bus 監控從 ldrex 到 strex 之間有無其他的 CPU 或 DMA 來存取這個位址 ,若有的話,strex 會在第一個 register 裡設定值為 1(non-exclusive by this CPU) 並且令 store 動作失敗,若沒有,strex 會在第一個 register 裡設定值為 0(exclusive access by this CPU) 並且令 store 動作成功。

Code Trace Discussion: 

Line 1: __volatile__ 告訴 compiler ,不要對這塊 assembly template 做最佳化動作,因為我們裡面有 loop 讀取 memory 動作,最佳化的結果可能導致 compiler 用一個 register 來 cache 它的值,不會老老實實的去讀 memory... 呵呵,這不是我們想要的動作喔!

Line 2: 把 lock 讀到 tmp,並請 bus monitor 這個 memory

Line 3: 測試 lock 是否為 0,若非 0,表示 lock 已經被別人取得了,則 Line 4,5 都不做了,然後  Line 6 一定 branch,做 spin 的動作。若為 0,表示有機會取得 lock,繼續做 Line 4.5. 

Line 4: 重點來了!,核對 bus monitor 的結果,若是exclusive access 則 tmp 設為 0 並且把 1 儲存到 lock,若是 non-exclusive access(有其他 CPU 來動過) 則 tmp設為 1並且不做儲存 lock 的動作。

Line 5: 測試 tmp

Line 6: 若 tmp 為 0 表示剛剛對 lock 動作是 exclusive,可以離開迴圈,若 tmp 為 1,則做 spin 動作。

Line 7: tmp 用 register 來操作,同時是 input 及 output 令它為 %0

Line 8: &lock->lock 用 register 來操作 ,令它為 %1,值 1 用用 register 來操作 ,令它為 %2

Line 9: 此 template 會改到 condition code,加入 clobber list 以告訴 compiler 有這回事

好了,終於看完了,真的很佩服那些 coding 的 kernel hackers ....

思考問題: ARM v6 以前有個  SWP 指令可以 lock bus and swap memory ,一樣可以用來完成 exclusive access ,但是比起 ldrex,strex 這對指令有什麼缺點呢?

ANS: 用滑鼠反白下幾行

SWP lock bus,其他 CPU 所有動作都不能做,但是 ldrex,strex就不會有這種現象,使用 ldrex,strex 時若其他 CPU不來 access 這個特定的 memory 就可以平行的做動作,增加平行執行的 performance


原文链接:http://blog.roodo.com/use_the_force/archives/3420371.html.

<think>好的,我现在需要给用户介绍`ldrex`指令。首先,用户之前问过`volatile`关键字,现在转向`ldrex`,可能是在学习底层编程或多线程同步机制。`ldrex`属于ARM架构的指令,所以用户可能在使用ARM处理器,或者对嵌入式开发感兴趣。 首先,我需要确认`ldrex`的基本信息。LDREX代表Load Exclusive Register,是ARM中的独占加载指令,通常与STREX(Store Exclusive Register)配对使用,用于实现原子操作。这一点很重要,因为用户可能了解过`volatile`在多线程中的不足,转而寻求更可靠的原子操作机制。 接下来,我需要解释LDREX的工作原理。LDREX从内存加载值,并标记该内存区域为独占访问。之后,STREX会尝试存储,只有在独占状态仍有效时才会成功。这确保了操作的原子性。需要举例说明,比如自增操作,防止多线程竞争。 然后,应用场景。用户可能关心何时使用这个指令。常见场景包括无锁数据结构、自旋锁、资源计数等。这里要强调LDREX/STREX在嵌入式系统多核处理器中的重要性,特别是在没有操作系统支持的情况下实现同步。 注意事项部分,需要提到内存屏障,因为ARM架构可能有宽松的内存顺序,需要DMB或DSB指令来保证顺序。另外,循环重试的必要性,因为STREX可能失败,需重复操作直到成功。此外,与高级语言中的原子操作对比,比如C11的stdatomic或C++的atomic,说明LDREX是底层实现的一部分。 可能用户会混淆LDREXCAS(Compare-And-Swap),需要简要比较。LDREX/STREX更灵活,允许在加载存储之间执行其他操作,而CAS是单一原子操作。 最后,总结LDREX的作用,强调其在ARM架构中实现原子操作的重要性,以及在高并发实时系统中的价值。确保语言简洁,结构清晰,符合用户要求的逐步解析,避免使用Markdown,用自然的中文口语化表达。</think>`ldrex` 是 ARM 架构中的一条关键指令,全称为 **Load Exclusive Register**(独占加载寄存器)。它与 `strex`(Store Exclusive Register,独占存储寄存器)配合使用,用于实现**原子操作**(atomic operations),确保多核/多线程环境下对共享内存的安全访问。 --- ### 一、核心作用 1. **实现原子操作** - `ldrex` 从内存加载值到寄存器,并标记该内存地址为“独占访问状态”。 - 后续的 `strex` 指令会尝试向该地址写入新值,**仅当内存未被其他操作修改时**,写入才会成功。 - 这一组合操作保证了对共享数据的“读--写”过程是原子的。 2. **避免锁竞争** - 相比传统互斥锁,`ldrex/strex` 通过硬件支持的无锁机制减少线程阻塞,提升并发性能。 --- ### 二、工作原理 1. **执行流程** ```armasm ; 示例:原子递增操作 retry: LDREX R1, [R0] ; 1. 从地址R0加载值到R1,标记独占访问 ADD R1, R1, #1 ; 2. 修改值(例如递增) STREX R2, R1, [R0] ; 3. 尝试将R1的值存回R0,结果状态存入R2(0=成功,1=失败) CMP R2, #0 ; 4. 检查是否成功 BNE retry ; 5. 失败则重试 ``` - 若其他线程/核心在 `ldrex` `strex` 之间修改了目标内存,`strex` 会失败(R2=1),需重试整个操作。 2. **独占监视器(Exclusive Monitor)** - ARM 内核通过硬件级“独占监视器”跟踪 `ldrex` 标记的内存区域,检测是否被其他操作干扰。 --- ### 三、典型应用场景 1. **无锁数据结构** - 实现线程安全的计数器、队列或栈,无需互斥锁。 ```c // C代码示例(内联汇编) void atomic_increment(int* value) { int tmp; do { __asm__ volatile("ldrex %0, [%1]" : "=r"(tmp) : "r"(value)); tmp++; } while (__asm__ volatile("strex %0, %1, [%2]" : "=r"(result) : "r"(tmp), "r"(value))); } ``` 2. **自旋锁(Spinlock)** - 利用 `ldrex/strex` 实现轻量级锁,适用于短期资源争用。 ```armasm ; 自旋锁获取 lock_acquire: LDREX R1, [R0] ; R0指向锁变量 CMP R1, #0 ; 检查是否已锁定 STREXEQ R1, #1, [R0] ; 尝试加锁 CMPEQ R1, #0 ; 判断是否成功 BNE lock_acquire ; 失败则重试 DMB ; 内存屏障确保操作顺序 ``` 3. **多核资源同步** - 在多核系统中协调共享硬件资源(如外设寄存器、内存池)。 --- ### 四、注意事项 1. **内存屏障需求** - ARM 架构允许指令重排,需配合 `DMB`(数据内存屏障)或 `DSB`(数据同步屏障)确保操作顺序。 $$例:在\ `strex`\ 后插入\ `DMB`\ 保证写入对其他核心可见。$$ 2. **循环重试机制** - `strex` 可能因竞态条件失败,必须通过循环重试保证最终成功。 3. **作用域限制** - 独占标记的作用范围通常限于特定内存区域(如缓存行),跨区域操作可能失效。 4. **与高级语言对比** - 在 C11/C++11 中,`stdatomic.h` 或 `<atomic>` 库的原子操作(如 `atomic_fetch_add`)可能由 `ldrex/strex` 底层实现--- ### 五、与其他指令的对比 | 指令/机制 | 特点 | |-----------------|----------------------------------------------------------------------| | **ldrex/strex** | 硬件级原子支持,无锁,需手动循环重试 | | **CAS指令** | 其他架构(如x86)的原子比较交换指令,功能类似但实现方式不同 | | **互斥锁** | 软件级锁,简单但可能引发线程阻塞上下文切换开销 | --- ### 六、总结 `ldrex` 是 ARM 架构中实现**无锁并发**的核心指令,通过硬件支持的独占访问机制,为多核编程提供高效原子操作。其典型应用场景包括自旋锁、计数器、无锁队列等,是高性能嵌入式系统实时系统的关键底层工具。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值