分类:
2009-12-30 17:07:31
为什么要对内存访问操作顺序进行重排?
简单的答案是:为了性能。CPU现在已经快得离谱,几个M的缓存也满足不了CPU的需要了。所以Cache经常被划分为几乎相互独立的几个bank。这样多个bank就可以并行工作,以减小缓存和CPU之间的性能差距。Memory按照地址被划分成几块,对应到不同cache bank。例如所有标号为偶数的cache line由bank 0处理,奇数标号的由bank 1处理。但是这种硬件层面上的并行也有坏处:内存操作可能不按指定的顺序完成。这可能会造成一些混乱。例如CPU0可能首先向0x12345000写入内容,这是一个奇数编号的cache line,假设它由bank0处理;然后再向0x12345100写入内容,这是一个偶数编号的cache line,假设它由bank1处理。如果bank0当时正忙于一些前面尚未完成的操作,而bank1是空闲的,这样CPU1就很可能先看到第二个写操作的结果。换句话说,CPU1“感知”到的这两个写操作的顺序,就和我们的预期不同。读操作也有可能类似地被打乱顺序。这种乱序可能引起教科书上的很多并行算法失败。
内存操作顺序重排和对称多处理器系统下的软件
一部分机器能够提供“顺序一致性”,即所有内存操作按照代码指定的顺序发生,而且系统中所有CPU所见的内存操作全局顺序完全一致。顺序一致的系统有很多优点,但往往性能不高。全局的顺序一致性极大地限制了硬件的并行处理能力,因此商用的CPU和系统不提供顺序一致性。
在这样的系统上,要区分一下三个顺序:
1. 程序顺序:某CPU上运行的程序代码中指定的内存操作顺序。
2. 执行顺序:某CPU执行内存访问指令的顺序。执行顺序可能和程序顺序不同,因为编译器和CPU都可能基于性能优化的需要,对内存操作进行顺序重排。
3. 感知顺序:某CPU“感知”到的、自身和其他CPU上的内存操作顺序。由于缓存、CPU间的连接和内存系统的某些优化措施,感知顺序可能和执行顺序不同。对同一内存操作指令序列来说,不同的CPU感知到的顺序可能是不同的。
比较流行的内存一致性模型包括:x86的进程一致性模型(某一CPU上的写操作,在所有CPU上感知到的顺序都相同);弱一致性模型(显式指定的两个内存屏障指令之间的所有内存操作,顺序可以任意重排)。谈到内存操作顺序重排在不同CPU上的效果时,总是既有好消息也有坏消息。坏消息是,每个CPU的内存操作顺序重排方式都有独特之处;好消息是,你总是可以认为下列条件成立:
1. 一个CPU对发生在其上的内存操作,感知顺序总是和程序顺序相同。即内存访问操作顺序重排带来的问题,仅当一个CPU在观察其他CPU的内存操作时才会出现。
2. 仅当某指令和另外一个存储指令访问的不是同一个地址,他们的顺序才可以被打乱。
3. 地址对齐的装载和存储指令都是原子操作。
4. Linux内核的同步原语包含了所需的任何内存屏障指令,使用它们的时候就不必担心乱序问题。Spinlocks,semaphores,RCU都无需再显式使用内存屏障;只有那些比较微妙的、不使用这些同步原语的代码才需要内存屏障。特别需要注意的是,多数原子操作,如atomic_inc()和atomic_add(),并不包含内存屏障。
Linux如何解决内存操作乱序带来的问题?
Linux能运行在很多种不同的CPU上,遗憾的是,这些CPU所支持的内存一致性模型并不相同,Linux的内核是如何做到可移植的呢?
Linux提供了一些精挑细选的内存屏障原语:
smp_mb():一个既用于限制读操作也用于限制写操作的内存屏障。该屏障前的读写操作保证会在屏障后的任何读写操作之前完成。
smp_rmb():内存读屏障。只用于限制内存读取操作。
smp_wmb():内存写屏障。只用于限制内存写入操作。
smp_read_barrier_depends():对其后续操作中所有依赖于其前导操作的部分,强制其顺序不变。这个原语在Alpha以外的所有平台上都是一个空操作。
smp_mb(),smp_rmb()和smp_wmb()原语还强制编译器放弃优化措施,如果该优化措施会对跨越屏障两边的内存操作顺序进行重排。smp_read_barrier_depends()在Alpha CPU上也有同样的功能。
这些原语仅在SMP的内核上才生成相应代码,而且每个都有一个对应的单处理器版本: mb(),rmb(),wmb(),read_barrier_depends()——这些单处理器版本在单处理器的内核中也生成内存屏障代码。绝大多数场合都应该用smp_开头的版本。单处理器的版本在写设备驱动的时候有用处,因为内存映射I/O的内存访问在单处理器的内核中也要保证顺序不变。没有这些内存屏障原语,编译器和CPU硬件都可能重排内存操作顺序,其结果,好的时候是让设备工作不正常;坏的就能让内核崩溃甚至损坏硬件。