---
title: 注意架构差异
description: "举一个自旋锁的移植来说明这一点，假设在x86平台中有一个自旋锁实现如下所示："
url: https://www.hikunpeng.com/document/detail/zh/kunpenggrf/codeprtr/kunpengtaishanporting_12_0051.html
sourcePath: /source/zh/kunpenggrf/codeprtr/kunpengtaishanporting_12_0051.html
indexId: ffeaa996f34ff52f7091de7a04340e6267c6ab35e01423804c40b6ea7ea5bccf65
---
# 注意架构差异

举一个自旋锁的移植来说明这一点，假设在x86平台中有一个自旋锁实现如下所示：

```
#define barrier() __asm__ __volatile__("": : :"memory")
int CompareAndSwap(volatile int* ptr,
int old_value,
int new_value) {
int prev;
__asm__ __volatile__("lock; cmpxchgl %1,%2"
: "=a" (prev)
: "q" (new_value), "m" (*ptr), "0" (old_value)
: "memory");
return prev;
}
static void lock(int *l){
while(CompareAndSwap(l, 0, 1) != 0);
}
static void unlock(int volatile *l){
barrier();
*l = 0;
}
```

#### 指令集差异

这是一个简化的自旋锁实现，在我们对CompareAndSwap这个函数进行移植时，我们首先关注到的是两个架构中指令集的差异。在这个实现中，通过内联汇编语法使用了x86架构下的cmpxchgl指令，但是在ARM架构下并没有与之完全一一对应的指令。

在ARM架构下，原子操作是通过exclusive指令对实现的，如下图所示（图片来自ARMv8体系结构参考手册(https://developer.arm.com/docs/ddi0487/latest/arm-architecture-reference-manual-armv8-for-armv8-a-architecture-profile)）：


所以我们使用exclusive指令对来实现这个CompareAndSwap函数，如下所示：

```
int CompareAndSwap(volatile int* ptr,
int old_value,
int new_value) {
int prev;
int temp;
__asm____volatile__ (
"0:                                    \n\t"
"ldxr %w[prev], %[ptr]                 \n\t"
"cmp %w[prev], %w[old_value]           \n\t"
"bne 1f                                \n\t"
"stxr %w[temp], %w[new_value], %[ptr]  \n\t"
"cbnz %w[temp], 0b                     \n\t"
"1:                                    \n\t"
: [prev]"=&r" (prev),
[temp]"=&r" (temp),
[ptr]"+Q" (*ptr)
: [old_value]"IJr" (old_value),
[new_value]"r" (new_value)
: "cc", "memory"
);
return prev;
}
```


#### 内存序差异

在上述替换CompareAndSwap函数之后，发现自旋锁并没有按预期工作，其主要是x86架构和ARM中的内存序差异导致的。

修改前：

```
static void lock(int *l){
while(CompareAndSwap(l, 0, 1) != 0);
}
static void unlock(int volatile *l){
barrier();
*l = 0;
}
```

如表1所述，在ARM架构下允许原子操作和内存读写之间的乱序，导致了上述代码中lock函数之后的内存访问可能被乱序到lock中的原子操作获取到锁之前执行，进而导致了非预期程序行为。

另一方面，在释放锁的时候，原代码中使用了一个编译型内存屏障，但是在ARM更宽松的内存序模型下不足以保证正确，需要改为CPU级的内存屏障，

修改后代码如下所示：

```
#define smp_mb()  asm volatile("dmb ish" ::: "memory")
static void lock(int *l){
while(CompareAndSwap(l, 0, 1) != 0);
smp_mb();
}
static void unlock(int volatile *l){
smp_mb();
*l = 0;
}
```

至此该自旋锁在功能上的移植就完成了，经过验证这个实现可以按预期工作。实际上，根据尽可能使用acquire和release语义进行同步中所述，我们可以通过半屏障来进一步优化这个锁的实现，提升锁的性能。在获取锁的时候只需要acquire语义，而释放锁的时候只需要release语义，从而去掉上面所使用的CPU级的全屏障。最终移植到ARM架构下的实现如下所示：

```
int CompareAndSwap(volatile int* ptr,
int old_value,
int new_value) {
int prev;
int temp;
__asm____volatile__ (
"0:                                    \n\t"
"ldaxr %w[prev], %[ptr]                 \n\t"
"cmp %w[prev], %w[old_value]           \n\t"
"bne 1f                                \n\t"
"stxr %w[temp], %w[new_value], %[ptr]  \n\t"
"cbnz %w[temp], 0b                     \n\t"
"1:                                    \n\t"
: [prev]"=&r" (prev),
[temp]"=&r" (temp),
[ptr]"+Q" (*ptr)
: [old_value]"IJr" (old_value),
[new_value]"r" (new_value)
: "cc", "memory"
);
return prev;
}
static void lock(int *l) {
while(CompareAndSwap(l, 0, 1) != 0);
}
static void unlock(int volatile *l)
{
int zero = 0;
__atomic_store(l, &zero, __ATOMIC_RELEASE);
}
```
