基于锁的原子操作
本文翻译自《英特尔® 64 和 IA-32 架构软件开发人员手册》卷三”系统编程指南”的第八章”多处理器的管理”的第一节 译者:方腾飞
8.1 基于锁的原子操作
32位IA-32处理器的系统内存地址支持基于锁的原子操作。这些操作通常用于管理共享的数据结构(如信号量,段描述符,系统段,或页表),在这些数据结构里,两个或多个处理器可以尝试同时修改相同的字段或标志。处理器使用三个相互关联的机制来实现基于锁的原子操作:
- 保证在单处理器下该操作是原子操作。
- 总线锁定,使用LOCK#信号和LOCK指令前缀。
- IMEI(高速缓存一致性)协议,以确保缓存的数据结构(高速缓存锁定)可以进行原子操作,这个机制是在奔腾4,英特尔至强和P6系列处理器。
这些机制在以下方面相互依存。一些基本的内存事务(如在系统内存中读取或写入一个字节)能保证总是被原子的处理。也就是说,一旦某个处理器开始操作某个内存地址,处理器保证在其他处理器或总线代理访问内存地址之前完成这个操作。为了实现多个内存操作(如在共享内存区进行读-修改-写操作),处理器同时也支持总线锁定。通常需要以原子的方式进行处理,但处理器不采取自动的方式处理,因为频繁使用的内存往往被缓存在L1 或L2 里,原子操作通常能在处理器的缓存内部执行,而不使用总线锁。当对被缓存的内存位置执行原子操作时,处理器的缓存一致性协议保证其他缓存同样内存位置的处理器被妥善的管理。
8.1.1 保证原子操作
英特尔486处理器(和更新的处理器)保证以下基本内存操作将始终以原子方式进行:
- 读取或写入一个字节。
- 读或写一个字一个16位边界上对齐。
- 读取或写入一个32位的边界上对齐双字。
奔腾处理器(和更新的处理器)保证以下额外的内存操作将始终以原子方式进行:
- 读取或写入一个64位的边界上对齐的四倍字长。
- 适合在一个32位的数据总线的16位访问未缓存的内存地址。
P6系列处理器(和更新的处理器)保证以下额外的内存操作将始终以原子方式进行:
- 未对齐16 ,32 和64位的访问填充在缓存行中缓存。
如果访问缓存被分成不同的高速缓存行和页边界里,将不保证是原子性。英特尔酷睿2双核,英特尔®凌动™处理器Intel酷睿,双核,奔腾4,奔腾M,英特尔至强处理器,P6系列,奔腾,Intel486处理器,英特尔Core 2 Duo处理器,英特尔凌动,英特尔酷睿,奔腾M,奔腾4,英特尔至强,和P6系列处理器提供总线控制信号允许外部内存的子系统使分裂访问原子,但是非对齐的数据访问严重影响性能的处理器,应该避免。一个x87指令或一个SSE指令实现多个存储器的访问比用四字实现要大。如果这样的指令存储在内存里,一些访问(写内存)或许能够完成,当另外一个操作因为架构原因失败。(例如,由于一个页表项故障被标记为“不存在”)。
8.1.2 总线锁定
英特尔64和IA-32处理器提供了一个LOCK#信号,这个信号在某些关键的内存操作时自动发出锁定系统总线或等价链路。当此输出信号被发出时,从其它处理器或控制总线代理的请求会被阻塞。软件可以指定其他的场合时,LOCK
语义应遵循在前面加上LOCK前缀的指令。
在英特尔386,英特尔486和奔腾处理器,显式锁定的情况下,说明会导致发出LOCK#信号。它的责任是硬件设计者在系统硬件中使用LOCK#信号控制处理器中内存访问。
对于P6和更新的处理器家族,如果被访问的内存区域是处理器内部缓存,一般不主张发出LOCK#信号,相反,锁定只适用于处理器的高速缓存。
8.1.2.1 自动锁定
8.1.2.2 总线锁定控制软件
8.1.3 Handling Self- and Cross-Modifying Code
(译者注:以上三个小节未翻译,有兴趣的朋友可以帮忙补充下)
8.1.4 处理器内部高速缓存锁操作的影响
在英特尔486和奔腾处理器里,即使被锁定的内存区域是缓存在处理器中,LOCK#信号在LOCK操作期间总是声言在总线上。
在P6和最近的处理器中,如果在锁操作期间,已经缓存在处理器缓存行的内存区域执行锁操作回写内存,处理器不会在总线上声言LOCK#信号。相反地,它会修改内部内存地址,并使用缓存一致性机制来确保执行的原子性,此操作被称为“缓存锁定”,缓存一致性机制会阻止同时修改被两个以上处理器缓存的内存区域数据。
原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: 基于锁的原子操作
Good job! Thanks!
顶一个。
不管什么语言,多线程都是由这些基础的硬件技术实现