每条汇编指令原子_并发原理系列六:原子操作与 x86 上的 lock 指令前缀

原子操作是不可分割的操作,在执行完毕时它不会被任何事件中断。在单处理器系统(UniProcessor,简称 UP)中,能够在单条指令中完成的操作都可以认为是原子操作,因为中断只能发生在指令与指令之间。

在多处理器系统(Symmetric Multi-Processor,简称 SMP)中情况有所不同,由于系统中有多个处理器在独立的运行,即使在能单条指令中完成的操作也可能受到干扰。

在所有的 X86 CPU 上都具有锁定一个特定内存地址的能力,当这个特定内存地址被锁定后,它就可以阻止其他的系统总线读取或修改这个内存地址。这种能力是通过 LOCK 指令前缀再加上下面的汇编指令来实现的。当使用 LOCK 指令前缀时,它会使 CPU 宣告一个 LOCK# 信号,这样就能确保在多处理器系统或多线程竞争的环境下互斥地使用这个内存地址。当指令执行完毕,这个锁定动作也就会消失。

能够和 LOCK 指令前缀一起使用的指令如下所示:

BT, BTS, BTR, BTC (mem, reg/imm)

XCHG, XADD (reg, mem / mem, reg)

ADD, OR, ADC, SBB (mem, reg/imm)

AND, SUB, XOR (mem, reg/imm)

NOT, NEG, INC, DEC (mem)

注意:XCHG 和 XADD (以及所有以 'X' 开头的指令)都能够保证在多处理器系统下的原子操作,它们总会宣告一个 "LOCK#" 信号,而不管有没有 LOCK 前缀。

使用原子操作的一个简单示例如:

void __fastcall atomic_inc (volatile int* pNum)
{__asm
{
lock inc dword ptr [ECX]
ret
}
}

上面, __fastcall 关键字确保参数是通过寄存器来传递的,这样就能够提升原子指令的性能。

另外,在 linux 内核中有一套原子操作函数,比如其中之一:

static __inline__ void atomic_add(int i, atomic_t *v)

{

__asm__ __volatile__(

LOCK_PREFIX "addl %1,%0"

:"+m" (v->counter)

:"ir" (i));

}

上面,LOCK_PREFIX 宏定义了 LOCK 指令前缀,如:
#ifdef CONFIG_SMP

#define LOCK_PREFIX

".section .smp_locks,"a""

" .align 4"

" .long 661f" /* address */

".previous"

"661:lock; "

#else /* ! CONFIG_SMP */

#define LOCK_PREFIX ""

#endif

由上可见,在单处理器系统下,LOCK_PREFIX 宏为空,因为此时并不需要 LOCK 指令前缀,处理器只要有可能,原子操作就会被编译成单个机器指令。

在一些处理器,包括 P6 家族,奔腾4(Pentium4)系列,至强(Xeon)处理器,lock 操作可能不会宣告一个 LOCK# 信号。从 P6 家族处理器开始,当使用 LOCK 指令访问的内存已经被处理器加载到缓存中时,LOCK# 信号通常不会被宣告。取而代之的是,仅是锁定了处理器的缓存。这里,处理器缓存的相干性(coherency)机制确保了可以原子性的对内存进行操作。

1417062a-4c24-eb11-8da9-e4434bdf6706.png
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值