原子操作与 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\"\n"     \
         "   .align 4 \n "            \
"   .long 661 f \n " /* address */    \
" .previous \n "            \
" 661 : \n \ tlock; "

#else /* ! CONFIG_SMP */
#define LOCK_PREFIX " "
#endif

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


在一些处理器,包括 P6 家族,奔腾4(Pentium4)系列,至强(Xeon)处理器,lock 操作可能不会宣告一个 LOCK# 信号。从 P6 家族处理器开始,当使用 LOCK 指令访问的内存已经被处理器加载到缓存中时,LOCK# 信号通常不会被宣告。取而代之的是,仅是锁定了处理器的缓存。这里,处理器缓存的相干性(coherency)机制确保了可以原子性的对内存进行操作。
  • 9
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
x86指令中,BTS指令可以用来实现简单的锁机制。它可以用于原子地设置或清除某个位,因此可以用于控制一个共享资源的访问。另外,通过在BTS指令前添加LOCK前缀,可以将其变成一个原子操作,确保多个CPU同时访问该共享资源时不会出现竞争条件。 下面是一个简单的示例,演示如何使用BTS指令LOCK前缀实现锁机制: ``` section .data lock_var dd 0 section .text global lock_func lock_func: mov eax, 1 mov edx, 0 lock bts dword [lock_var], edx jnc locked ret locked: ; 在这里执行临界区代码 ; 然后释放锁 mov eax, 0 mov edx, 0 lock btr dword [lock_var], edx ret ``` 在这个示例中,我们定义了一个32位的共享变量lock_var,并将其初始化为0。接下来,我们定义了一个名为lock_func的函数,它用于获取锁。首先,我们将1赋给EAX寄存器,将0赋给EDX寄存器,然后使用LOCK BTS指令lock_var的第0位设置为1。如果BTS指令成功执行,CF标志位将被清除,表示锁已经获取。否则,如果CF标志位被设置,表示锁已经被其他线程占用,我们将直接返回。 在临界区代码执行完毕后,我们需要释放锁。这可以通过将0赋给EAX寄存器,0赋给EDX寄存器,然后使用LOCK BTR指令lock_var的第0位清除来实现。这样可以确保其他线程可以获取锁并继续执行。 需要注意的是,这个示例只是一个简单的演示,并不考虑多线程竞争的情况。在实际使用中,需要根据具体的应用场景进行合理的设计和实现

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值