cpu实现原子性-总线锁和缓存锁


随着多核时代的到来,并发操作已经成了很正常的现象,cpu处理器实现原子操作有两种方式:(1)使用总线锁保证原子性。(2)使用缓存锁保证原子性。

1.总线锁

总线锁就是将cpu和内存之间的通信锁住,使得在锁定期间,其他cpu处理器不能操作其他内存中数据,故总线锁开销比较大。

总线锁的实现是采用cpu提供的LOCK# 信号,当一个cpu在总线上输出此信号时,其他cpu的请求将被阻塞,那么该cpu则独占共享内存。

何为总线?

CPU总线是所有CPU与芯片组连接的主干道,负责CPU与外界所有部件的通信,包括高速缓存、内存、北桥,其控制总线向各个部件发送控制信号、通过地址总线发送地址信号指定其要访问的部件、通过数据总线双向传输。

2.缓存锁

缓存锁是采用“缓存锁定”将原子操作放在cpu缓存中进行(L1、L2、L3高速缓存)。“缓存锁定”指当发生共享内存的锁定,处理器不会在总线上声言LOCK#信号,而是修改内存地址,并通过缓存一致性机制保证原子性。因为缓存一致性机制会阻止同时修改由两个以上处理器缓存的内存区域数据,当其他处理器回写已被锁定的缓行的数据时,会使缓存行无效

缓存一致性机制?

缓存一致性机制就整体来说,是当某块CPU对缓存中的数据进行操作了之后,就通知其他CPU放弃储存在它们内部的缓存,或者从主内存中重新读取。

MESI协议?

是以缓存行(缓存的基本数据单位,在Intel的CPU上一般是64字节)的几个状态来命名的(全名是Modified、Exclusive、 Share or Invalid)。该协议要求在每个缓存行上维护两个状态位,使得每个数据单位可能处于M、E、S和I这四种状态之一,各种状态含义如下:
M:被修改的。处于这一状态的数据,只在本CPU中有缓存数据,而其他CPU中没有。同时其状态相对于内存中的值来 说,是已经被修改的,且没有更新到内存中。
E:独占的。处于这一状态的数据,只有在本CPU中有缓存,且其数据没有修改,即与内存中一致。
S:共享的。处于这一状态的数据在多个CPU中都有缓存,且与内存一致。
I:无效的。本CPU中的这份缓存已经无效。

总结四种状态:可分为两种 独占(M和E)共享(S和I)。

独占:M是只有本cpu有,而且缓存已被修改,与内存不一致;E是只有本cpu有,缓存未修改和内存一致。

共享:S是多cpu缓存中都有,该缓存未修改与内存一致;I是多cpu缓存中都有,该缓存修改与内存不一致,该缓存失效。

一个处于M状态的缓存行,必须时刻监听所有试图读取该缓存行对应的主存地址的操作,如果监听到,则必须在此操作执行前把其缓存行中的数据写回CPU。

一个处于S状态的缓存行,必须时刻监听使该缓存行无效或者独享该缓存行的请求,如果监听到,则必须把其缓存行状态设置为I。

一个处于E状态的缓存行,必须时刻监听其他试图读取该缓存行对应的主存地址的操作,如果监听到,则必须把其缓存行状态设置为S。

​ 当CPU需要读取数据时,如果其缓存行的状态是I的,则需要从内存中读取,并把自己状态变成S,如果不是I,则可以直接读取缓存中的值,但在此之前,必须要等待其他CPU的监听结果,如其他CPU也有该数据的缓存且状态是M,则需要等待其把缓存更新到内存之后,再读取。

​ 当CPU需要写数据时,只有在其缓存行是M或者E的时候才能执行,否则需要发出特殊的RFO指令(Read Or Ownership,这是一种总线事务),通知其他CPU置缓存无效(I),这种情况下性能开销是相对较大的。在写入完成后,修改其缓存状态为M。

3.两种情况下处理器不会使用缓存锁定

第一种情况是:当操作的数据不能被缓存在处理器内部,或操作的数据跨多个缓存行(cache line)时,则处理器会调用总线锁定。
第二种情况是:有些处理器不支持缓存锁定。对于Intel 486和Pentium处理器,就算锁定的
内存区域在处理器的缓存行中也会调用总线锁定

  • 12
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
实现原子操作时,需要考虑内存对齐和缓存一致性等问题,主要可以通过以下方法进行处理: 1. 内存对齐 为了确保原子操作的正确性和效率,需要对内存进行对齐。在 C 语言中,可以使用 `__attribute__((aligned(n)))` 来指定变量的对齐方式,其中 `n` 表示对齐的字节数。例如: ```c int __attribute__((aligned(8))) a; // 以 8 字节对齐 ``` 2. 缓存一致性 为了确保原子操作的正确性,需要使用一些特殊的指令或者技术来确保数据在不同缓存之间的一致性。常见的方法包括: - 使用 CPU 提供的原子操作指令,例如 x86 平台的 `lock` 前缀指令。 - 使用一些特殊的内存屏障指令,例如 x86 平台的 `mfence` 指令和 `clflush` 指令。 - 使用机制,例如互斥和读写等。 下面是一个使用 GCC 内置函数 `__sync_fetch_and_add()` 实现原子操作的例子,其中使用了内存对齐和缓存一致性技术: ```c #include <stdio.h> #include <stdint.h> int main() { int32_t __attribute__((aligned(64))) value = 0; // 以 64 字节对齐 __sync_fetch_and_add(&value, 1); // 原子地对 value 加 1 printf("value = %d\n", value); return 0; } ``` 在上面的例子中,使用了 GCC 内置函数 `__sync_fetch_and_add()` 来实现原子操作,同时使用了 `aligned` 属性来指定变量的对齐方式。此外,在进行原子操作时, GCC 会自动插入一些内存屏障指令来保证缓存一致性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值