Chinaunix首页 | 论坛 | 博客
  • 博客访问: 988113
  • 博文数量: 158
  • 博客积分: 4380
  • 博客等级: 上校
  • 技术积分: 2367
  • 用 户 组: 普通用户
  • 注册时间: 2006-09-21 10:45
文章分类

全部博文(158)

文章存档

2012年(158)

我的朋友

分类: C/C++

2012-11-19 13:10:35

Intel CPU相关指令:
LOCK
这是一个指令前缀,在所对应的指令操作期间使此指令的目标操作数指定的存储区域锁定,以得到保护。
XADD
先交换两个操作数的值,再进行算术加法操作。多处理器安全,在80486及以上CPU中支持。
CMPXCHG
比较交换指令,第一操作数先和AL/AX/EAX比较,如果相等ZF置1,第二操作数赋给第一操作数,否则ZF清0,第一操作数赋给AL/AX/EAX。
多处理器安全,在80486及以上CPU中支持。
CMPXCHG8B
同上,64位比较交换指令,第二操作数隐含为EDX:EAX,比较EDX:EAX与64位的目标,如果相等则ECX:EBX送往目标且ZF置1,否则目标送EDX:EAX且ZF清0。

windows互锁API列表:
InterlockedCompareExchange/InterlockedCompareExchange64
InterlockedCompareExchangeAcquire/InterlockedCompareExchangeAcquire64
InterlockedCompareExchangePointer
InterlockedCompareExchangeRelease/InterlockedCompareExchangeRelease64
InterlockedDecrement/InterlockedDecrement64
InterlockedDecrementAcquire
InterlockedDecrementRelease
InterlockedExchange/InterlockedExchange64
InterlockedExchangeAcquire64
InterlockedExchangeAdd/InterlockedExchangeAdd64
InterlockedExchangePointer
InterlockedIncrement/InterlockedIncrement64
InterlockedIncrementAcquire
InterlockedIncrementRelease

  • 多处理器安全就是当某值被某处理器修改后,其他处理器应当知晓,而不再使用CPU缓存内的旧数据。
  • 本文不讨论WinNT3.51/Win95以及更早的操作系统(它们是为80386设计的),以及安装在80386上的Win98(Win98安装时自动判断是否支持XADD指令)。

看一个简单的函数,它的作用是将lpAddend加1,并返回之。
LONG InterlockedIncrement( LPLONG lpAddend )
{
    MOV           ecx, lpAddend
    MOV           eax, 1
    LOCK XADD     dword ptr [ecx], eax
    INC           eax
    RET           4
}

看一个复杂的函数,它的作用是将lValue赋值给*plTarget,并返回*plTarget的原先值。
LONG InterlockedExchange( LPLONG plTarget, LONG lValue )
{
    MOV           ecx, plTarget
    MOV           edx, lValue
    MOV           eax, dword ptr [ecx]
L:  LOCK CMPXCHG  dword ptr [ecx], edx
    JNE           L
    RET           8
}
看,不得不动用了一个循环指令。类似的还有InterlockedCompareExchange(pdestination,exchage,comperand)函数,如果Destination等于Comperand,将Exchange赋值给Destination,否则什么也不干,返回值为Destination的原先值。

循环技巧是很有用的,Jeffrey Richter给出了一个功能代码,如果值大于0,则加1,代码如下:
do {
    LONG lStartValue = *plTarget;
    LONG lNewValue = lStartValue + ((lStartValue > 0) ? 1 : 0);
} while( InterlockedCompareExchange(plTarget,lNewValue,lStartValue) != lStartValue );

一个未知的问题是,为什么在有的操作系统中没看到LOCK。未来的研究是线程/进程锁。

阅读(3708) | 评论(16) | 转发(0) |
给主人留下些什么吧!~~

网友评论2012-11-19 13:15:01

周星星
加入仅仅只需要交换,那么XCHG自然是可以的,但很多时候需要对plTarget指向的数据进行处理,处理完了之后再交换,那么这就是两个步骤了,在多处理单元下就可能出错,比如用CMPXCHG来实现+1的功能(当然,+1有更简单的方法。我只是用CMPXCHG事例一下):
假设*plTarget==0
LONG temp = *plTarget + 1;
XCHG ...
另一个处理单元也同时在做
LONG temp = *plTarget + 1;
XCHG ...
那么其中一种错误的步骤就是
CPU1:LONG temp = *plTarget + 1; // 即temp=1
CPU2:LONG temp = *plTarget + 1; // 即temp=1
CPU1:XCHG ... // 即 *plTarget=1
CPU2:XCHG ... // 即 *plTarget=1

网友评论2012-11-19 13:14:46

test
即为何不用如下代码(事实上MS的InterlockedExchange函数也为何不用下面的代码呢):
LONG InterlockedExchange( LPLONG plTarget, LONG lValue )
{
    MOV           ecx, plTarget
    MOV           eax, lValue
    LOCK XCHG   dword ptr [ecx], eax
    RET           8
}

网友评论2012-11-19 13:14:33

test
L:  LOCK CMPXCHG  dword ptr [ecx], edx
    JNE           L

目的是交换数据,为何不用如下代码
  LOCK XCHG  dword ptr [ecx], edx

请回复,谢谢.

网友评论2012-11-19 13:14:18

tongshou

星星, 再次谢谢您的答复!!! 这些困扰我多时的问题,今天终于弄清楚了!

网友评论2012-11-19 13:14:05

周星星
对于单CPU而言,同一时刻只有一个线程在运行,当时间片用完后,寄存器数据就被保存起来,用于下次再次运行此线程时的恢复。
所以 MOV eax, dword ptr [ecx]
L: LOCK CMPXCHG dword ptr [ecx], edx
你不需要关心寄存器,唯一要关心的是内存数据。

lStartValue (或eax) 所得的值是不确定的,但这个“不确定”不是你认为的那样是错乱的。比如同时执行ecx=1和ecx=2的话,ecx可能等于1,也可能等于2,但永远不可能等于1和3之外的数。也就是每个CPU指令不可能跨时间片去运行。

对于多CPU而言,MOV eax, dword ptr [ecx] 不存在问题,因为这只是读,没有改写;有问题的是 LOCK CMPXCHG dword ptr [ecx], edx,所以这里加上了LOCK,LOCK就是在多CPU下锁住目标操作数指定的存储区域,不让它同时进行。