Intel 64和IA-32处理器提供了LOCK#信号,在某些关键的访存操作时会自动地激活assert这个信号,用于封锁系统总线或类似的链接。当这个输出信号被激活时,就会阻塞来自于其他的处理器或总线代理的总线控制请求。在其他情况下,如果软件业务逻辑需要封锁总线,可以在相应的指令前放置一个LOCK指令前缀。
对于Intel386,Intel486,和Pentium处理器,显示地指定LOCK前缀,会使处理器激活LOCK#信号。硬件设计人员有责任在设计硬件时,提供LOCK#信号的接收机制,用来控制处理器间的访存操作。
对于P6及其后代处理器,如果访存操作命中处理器的内部缓存,在LOCK#信号通常不会被激活;而且,“加锁操作”只会发生在处理器的内部缓存中。
在以下操作中,处理器自动遵循“加锁的”的语义(即执行原子操作):
注:参看下图,以数据段描述符为例。
要显式地指示加锁语义,软件可以使用LOCK指令前缀。不是所有的指令都可以加LOCK前缀。下列的指令在操作存储器操作数时,可以使用LOCK前缀。当在其他指令前或者下述的指令前但是没有向内存写入数据(即目标操作数是寄存器)时,使用LOCK前缀会导致处理器产生无效操作码异常(#UD)。
对于加锁的指令,处理器提供的保证是目标操作数的内存区域一定是原子性操作;但是系统可能将这个区域解释为更大的范围(注:例如,一个缓存行)。
注意:不要使用WC类型的内存实现信号量。对用来实现信号量的缓存行,不要使用非时效存储操作。
相对于其他的访存操作与其他的外部可见事件,加锁的操作时原子性的。只有取指与页表访问可以跨越加锁的指令。因此,加锁的指令可以用于同步一个处理器的数据写入与另一个处理器的数据读取。
对于P6 family处理器,加锁的操作会串行化所有未完成的读存与写存操作(即等待所有的访存指令完成)。这个规则对于Pentium 4与Intel Xeon处理器也适用,只有一个例外:引用弱排序内存类型(例如WC类型的内存)的读操作不会被串行化。
加锁的指令不应该被用来保证写入的数据能被当作指令进行取指。
注意:当前版本的Pentium 4,Intel Xeon,P6 family,Pentium和Intel 486处理器允许写入的数据被当作指令读取。但是,Intel推荐想要使用自修改代码的开发者使用另一种同步机制达到这个目的。参看下节。
TODO
对于Intel486和Pentium处理器,在执行LOCK操作时,LOCK#信号总是被激活,即使被加锁操作的内存区域已经位于在处理器的缓存中。