处理器如何实现原子操作

一、并发和并行概念

原子(atomic):是“不能被进一步分割的最小粒子”,而原子操作(atomic operation)意为“不可被中断的一个或一系列操作”。

并发:单核cpu上,当多个线程在在操作时,把cpu运行时间划分为若干时间段,再将时间段分配给各个线程执行,在一个时间段的线程代码运行时,其他线程处于挂起状态。

并行:多核cpu上,当一个核心处理器执行一个线程时,另一个核心处理器可以同时个线程。

总结
单cpu计算机中进程只能是并发,多cpu计算机中进程可以并行。
单cpu单核计算机中线程只能并发,单cpu多核计算机中线程可以并行。

二、单核原子操作

C语言语句“count++;”在未经编译器优化时生成的汇编代码为。
在这里插入图片描述
当操作系统内存在多个进程同时执行这段代码时,就可能带来并发问题。
在这里插入图片描述
假设count变量初始值为0。进程1执行完“mov eax, [count]”后,寄存器eax内保存了count的值0。此时,进程2被调度执行,抢占了进程1的CPU的控制权。进程2执行“count++;”的汇编代码,将累加后的count值1写回到内存。然后,进程1再次被调度执行,CPU控制权回到进程1。进程1接着执行,计算count的累加值仍为1,写回到内存。虽然进程1和进程2执行了两次“count++;”操作,但是count实际的内存值为1,而不是2!

解决方法

解决这个问题的方法是,将“count++;”语句翻译为单指令操作:
在这里插入图片描述
Intel x86指令集支持内存操作数的inc操作,这样“count++;”操作可以在一条指令内完成。因为进程的上下文切换是在总是在一条指令执行完成后,所以不会出现上述的并发问题。对于单处理器来说,一条处理器指令就是一个原子操作。

三、多核原子操作

总线锁方式

在多处理器的环境下,例如SMP架构,这个结论不再成立。我们知道“inc [count]”指令的执行过程分为三步:

1)从内存将count的数据读取到cpu。

2)累加读取的值。

3)将修改的值写回count内存。

这又回到前面并发问题类似的情况,只不过此时并发的主题不再是进程,而是处理器。

核心1 核心1
mov eax,[count] 处理
处理 mov eax,[count]
处理 Inc eax
处理 mov [count],eax
Inc eax 处理
mov [count],eax 处理

Intel x86指令集提供了指令前缀lock用于锁定前端串行总线(FSB),保证了指令执行时不会受到其他处理器的干扰。
在这里插入图片描述
使用lock指令前缀后,处理器间对count内存的并发访问(读/写)被禁止,从而保证了指令的原子性。当一个CPU核执行一个线程去访问数据做操作的时候,它会向总线上发送一个LOCK信号,此时其他的线程想要去请求主内存的时候,就会被阻塞,这样该处理器核心就可以独享这个共享内存,而且释放锁后,会调用smp_mb invalide,使其他核心的缓存失效。

缓存锁方式(缓存一致性)

存储器结构

​​​​​​​​在这里插入图片描述
一般而言,从高层往底层走,存储设备变得更慢、更便宜和更大。在最高层(L0),是少量快速的CPU寄存器,CPU可以在一个时钟周期内访问它们。接下来是一个或多个小型到中兴的基于SRAM的高速缓存存储器,可以在几个CPU时钟周期内访问它们。然后是一个大的基于DRAM的主存,可以在几十到几百个时钟周期内访问它们。接下来是慢速但容量很大的本地磁盘。最后,有些系统甚至包括了一层附加的远程服务器上的磁盘,要通过网络来访问他们。

多核cpu的存储关系

在多核cpu中,每个核心(core)独享L0、L1、L2存储器,共享其他的存储器。
​​在这里插入图片描述
由于存在缓存区,有可能出现缓存区数据不一致的情况。例如,有一个变量a=0,现在起两个线程都对变量a执行加一操作,假如两个线程分别在两个核心运行,开始时两个核心都把a的值0存到了自己的缓存中。线程1从自己的缓存区读取a的值0,加1后写入主内存。线程2也是从自己的缓存区读取a的值0,加1后写入主内存。最后a的值是1,而不是2。

缓存一致性

当某核心对其缓存中的数据进行了操作之后,就通知其他核心放弃储存在它们内部的对应的缓存,或者从主内存中重新读取此数据。缓存一致性主要是通过MESI协议实现

MESI协议

MESI:缓存行(缓存的基本单位,在Intel的cpu上一般为64字节)的4种状态(Modified、Exclusive、 Share or Invalid)的缩写。该协议要求在每个缓存行上维护两个状态位,使得每个数据单位可能处于M、E、S和I这四种状态之一,协议各种状态含义如下:

M:被修改的。处于这一状态的数据,只在本CPU中有缓存数据,而其他CPU中没有。同时其状态相对于内存中的值来说,是已经被修改的,且没有更新到内存中。

E:独占的。处于这一状态的数据,只有在本CPU中有缓存,且其数据没有修改,即与内存中一致。

S:共享的。处于这一状态的数据在多个CPU中都有缓存,且与内存一致。

I:无效的。本CPU中的这份缓存已经无效

每个Core的Cache控制器不仅知道自己的读写操作,也监听其它Cache的读写操作,核心的读取会遵循几个原则:

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

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

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

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

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

Ps:并非所有情况都会使用缓存一致性的,
被操作的数据不能被缓存在处理器内部或操作数据跨越多个缓存行,处理器会调用总线锁定
有些处理器不支持缓存行锁定,只能用总线锁定,比如说奔腾486以及更老的CPU

CAS

CAS(Compare and Swap),cas记录原来内存中的值old,和将要修改的值new。CAS会检测现在内存中的值now,如果now和old一致,则说明没有别的cpu进行了内存修改,执行new值的更新。如果new和old值不等,则说明值已被修改,丢弃new值。

  • 6
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值