2020年1月

本文采用知识共享 署名-相同方式共享 4.0 国际 许可协议进行许可。
访问 https://creativecommons.org/licenses/by-sa/4.0/ 查看该许可协议。

缓存一致性

CPU的读写单元正常情况下不能直接访问内存,因为并没有管脚直接连接到内存。相反,CPU和一级缓存通讯,一级和二级,二级和三级...n级才能和内存通讯。

如只处理Read,事情会变很简单,因为

所有级别的缓存遵循:在任意时刻,任意级别缓存中的内容等同于它对应的内存中的内容.

如果考虑写操作

直写(遵循定律)

我们透过本级缓存,直接将数据写到下一级缓存,如当前对应段已被这级缓存了,则更新/丢弃。

回写

缓存不会立即吧写操作传递,仅把本级已修改段标记为脏段。脏段会触发回写(写入下一级), 脏段洗净。总结: 当一个脏段被丢弃时,总会先回写。
回写定律: 所有脏段回写后, 任意级别缓存中的内容等同于它对应的内存中的内容.

一致性协议

当单核情况时,没毛病。
如果有多个核, 且拥有自己的缓存(有些CPU共用一块缓存[太慢了]), 且为了解决回写带来的问题, 所以产生了MESI及其衍生协议.

  • Modified 已修改缓存段,当某个核心持有了M状态的缓存段, 其他拥有或等待拥有其内存段的其他核心中的拷贝会马上失效(I)
  • Exclusive 独占缓存段,与主存一致拷贝, 当某个核心持有了E状态的缓存段,其他拥有有其内存段的其他核心中的拷贝会马上失效(I), 且其他核心无法同时持有它
  • Shared 共享缓存段, 与主存一致拷贝, 此状态下的缓存段是只读的, 可被多个核心持有
  • Invalid 失效缓存段, 属于脏段,已不在缓存中/被其他核心M/E, 将会被忽略(相当于从来没被加载)

MESI 定律:在所有的脏缓存段(M 状态)被回写后,任意缓存级别的所有缓存段中的内容,和它们对应的内存中的内容一致。此外,在任意时刻,当某个位置的内存被一个处理器加载入独占缓存段时(E 状态),那它就不会再出现在其他任何处理器的缓存中。

有很多文章说“CPU乱序执行和预测执行”会导致最终写入到内存的数据不符合预期,事实是此结果会被丢弃。
参考:
CPU乱序执行和预测执行导致的安全问题
Wikipedia: Out-of-order execution

本文采用知识共享 署名-相同方式共享 4.0 国际 许可协议进行许可。
访问 https://creativecommons.org/licenses/by-sa/4.0/ 查看该许可协议。

二义性分为两种

  • 显式二义性
    a = ++a+++b
    # C++中会被编译器理解为++(a++)+y, 会引发编译异常
  • 隐式二义性
    b = ++a + (++a) + (++a)
    printf("%d %d %d", a, ++a, --a)
    #  表达式或多参函数中连续对同一变量自增自减, 在不同系统中得到的结果不一样

    Golang中不允许自增自减出现在运算和函数传参中, 只能单独使用-.-

Title - Artist
0:00