内存屏障

Posted by KANG's BLOG on Tuesday, March 15, 2022

一、介绍

由于编译器的优化和缓存的使用,导致对内存的写入操作不能及时的反应出来,也就是说当完成对内存的写入操作之后,读取出来的可能是旧的内容 — 摘自《独辟蹊径品内核》

可以理解为,内存屏障的目的就是为了防止上述情况的发生。

具体来说,内存屏障是一类同步屏障指令,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作。

二、内存屏障类型

  • LoadLoad屏障:

    对于这样的语句 Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。

  • StoreStore屏障:

    对于这样的语句 Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。

  • LoadStore屏障:

    对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被执行前,保证Load1要读取的数据被读取完毕。

  • StoreLoad屏障:

    对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的(冲刷写缓冲器,清空无效化队列)。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能。

三、内存屏障分类

  1. 可见性保障

    按照可见性保障来划分,内存屏障可分为:加载屏障(Load Barrier)和存储屏障(Store Barrier)。

    • 加载屏障:StoreLoad屏障可充当加载屏障,作用是刷新处理器缓存,即清空无效化队列,使处理器在读取共享变量时,先从主内存或其他处理器的高速缓存中读取相应变量,更新到自己的缓存中
    • 存储屏障:StoreLoad屏障可充当存储屏障,作用是冲刷处理器缓存,即将写缓冲器内容写入高速缓存中,使处理器对共享变量的更新写入高速缓存或者主内存中

    这两个屏障一起保证了数据在多处理器之间是可见的。

  2. 有序性保障

    按照有序性保障来划分,内存屏障分为:获取屏障(Acquire Barrier)和释放屏障(Release Barrier)。

    • **获取屏障:**相当于LoadLoad屏障LoadStore屏障的组合。在读操作后插入,禁止该读操作与其后的任何读写操作发生重排序;
    • **释放屏障:**相当于LoadStore屏障StoreStore屏障的组合。在一个写操作之前插入,禁止该写操作与其前面的任何读写操作发生重排序。

    这两个屏障一起保证了临界区中的任何读写操作不可能被重排序到临界区之外。