操作系统---图解系统(小林coding)

操作系统—图解系统(小林coding)

硬件结构

1.1 CPU是如何执行程序的

冯诺依曼模型

在这里插入图片描述

内存

我们的程序和数据都是存储在内存,存储的区域是线性的。

数据存储的单位是一个二进制单位(bit),即0或1。 最小的存储单位是字节(byte),1字节等于8位。

内存地址从0开始编号,自增排列,最后一个地址为内存总字节数-1,与数组类似,内存读写任何一个数据的速度都是一样的。

中央处理器CPU

常见的CPU有32和64位CPU,这个位数指的是CPU的位宽。32位CPU一次可以处理4字节,64位CPU一次可以处理8字节。

CPU位宽越大,可以计算的数值就越大。

寄存器

CPU中的寄存器主要作用是存储计算时的数据,CPU访问数据时先访问寄存器,再访问内存。

常见的寄存器种类:

  • 通用寄存器:存放需要进行运算的数据
  • 程序计数器:存储CPU要执行的下一条指令所在的内存地址
  • 指令寄存器:存放程序计数器指向的指令

总线

总线用于CPU 和内存以及其它设备之间的通信。

  • 地址总线:用于指定CPU将要操作的内存地址
  • 数据总线:用于读写内存的数据
  • 控制总线:用于接收和发送信号

CPU读写内存时,一般经过两个总线:首先通过地址总线来指定内存的地址,再通过数据总线来传输数据。

输入输出设备

输入设备向计算机输入数据,计算机通过计算后,把数据输出给输出设备。


线路位宽与CPU位宽

数据通过操作电压,低电压0,高电压1,进而实现通过线路传输。

一位一位传输的方式为串行,下一个bit必须等待上一个bit传输完成才能进行传输。与之相对的并行传输即一次传输多个bit,需要增加线路位宽。

为了避免低效率的串行传输的方式,线路的位宽最好一次就能访问到所有的内存地址

CPU位宽最好不要小于线路位宽。

如果计算的数额不超过32位数字的情况下,32位和64位CPU之间是没有什么区别的,只有当计算超过32位数字的情况下,64位的优势才能体现出来。


**程序执行流程:**一个程序执行时,CPU会根据程序计数器里的内存地址,从内存里面把需要执行的指令读取到指令寄存器中执行,然后根据指令长度自增,开始顺序读取下一条指令。

在这里插入图片描述

**CPU的指令周期:**CPU从程序计数器读取指令、到执行,再到下一条指令,这个过程会会不断循环直到程序执行结束,这个过程就叫做CPU的指令周期。

内存中有两个存放指令和数据的区域,分别是正文段数据段

在这里插入图片描述

**指令的编码:**编译器在编译程序时构造指令的过程。**指令的解码:**CPU执行程序时会解析指令的过程。


CPU通常使用流水线的方式来执行指令,分为四个阶段,这四个阶段称为指令周期

  • Fetch(取得指令):CPU通过程序计数器读取对应内存地址的指令
  • Decode(指令译码):CPU对指令进行解码
  • Execute(执行指令):CPU执行指令
  • Store(数据回写):CPU将计算结果存回寄存器或将寄存器的值存入内存

指令从功能角度划分为5大类:

  • 数据传输类型指令
  • 运算类型指令
  • 跳转型指令
  • 信号类型指令
  • 闲置类型指令

指令的执行速度

CPU的硬件参数GHz指的是时钟频率为1G,代表着1秒就会产生1G次数的脉冲信号,每一次脉冲信号高低电平的转换就是一个周期,称为时钟周期。

程序的CPU执行时间 = CPU时钟周期数 x 时钟周期时间 = 指令数 x CPI x 时钟周期时间

  • 时钟周期时间就是CPU主频,1/1G
  • CPU时钟周期数 = 指令数 x 每条指令的平均时钟周期数(CPI)

64位CPU相比于32位CPU的优势:

  • 64位CPU可以一次计算超过32位的数字,而32位CPU如果要计算超过32位的数字,要分多步骤进行计算,效率不高,但是大部分应用程序很少会计算大数字。所以,只有计算大数字时,64位CPU的优势才能体现出来,否则和32位CPU的计算性能相差不大。
  • 64位CPU可以寻址更大的内存空间为2^64,32位CPU的最大寻址地址是4G。

64位和32位软件,这里面的位指的是指令的位宽:

  • 如果32位指令在64位的机器上执行,需要一套兼容机制,就可以运行。但64位的指令在32位的机器上执行就会比较困难,因为32位的寄存器存不下64位的指令。
  • 操作系统也是一种程序,常见的32位和64位操作系统指的是操作系统中指令的位数,比如64位操作系统就不能装在32位机器上。

硬件的32和64位指的是CPU的位宽,软件的32和64位指的是指令的位宽。


1.2 存储器金字塔

存储器的层次结构:

在这里插入图片描述

  • **寄存器:**最靠近CPU的控制单元和逻辑计算单元的存储器。速度最快,价格最贵。32位CPU中大多数寄存器可以存储4个字节,64位CPU中大多数寄存器可以存储8个字节。

  • CPU Cache:使用一种SRAM(静态随机存储器)的芯片,有电数据则存在,断电数据就会丢失。
    在这里插入图片描述

    • L1高速缓存:访问速度几乎与内存器一样快,通常只需要2-4个时钟周期;指令和数据在L1是分开存放的,L1缓存通常分为指令缓存与数据缓存。
    • L2高速缓存:位置比L1高速缓存远,大小比L1大,访问速度更慢,通常为10-20个时钟周期。
    • L3高速缓存:通常是多个CPU核心共用的,位置比L2高速缓存还要远,大小更大,访问速度更慢,通常为20-60个时钟周期。
  • 内存:内存所使用的DRAM(动态随机存取存储器)芯片;相比于SRAM,DRAM的密度更高,功耗更低,容量更大,造价更便宜。访问速度为200-300个时钟周期。

  • SSD/HDD硬盘:SSD就是固体硬盘,断电后数据依然存在,访问速度比内存慢10-1000倍。HDD是机械硬盘,通过物理读写的方式来访问数据,访问速度比内存慢10w倍。

在这里插入图片描述

每个存储器之和相邻的一层存储器打交道,并且存储设备为了追求更快的速度,所需的材料成本必然也越高,因此,CPU内部的寄存器、L1、L2、L3 Cache只好使用较小的容量,而内存、硬盘则可使用较大的容量,这就是我们所说的存储器层次结构。


缓存体系:

在这里插入图片描述

不同的存储器之间性能差距很大,构造存储器分级很有意义,分级的目的是要构造缓存体系。


1.3如何写出让CPU跑得更快的代码

在之前的学习中,我们知道了为了弥补CPU和内存之间的性能差异,就在CPU和内存之间引入了CPU Cache,它通常分为大小不等的三级缓存即L1、L2、L3 Cache。而在L1 Cache中,数据和指令是分开缓存的,即数据缓存和指令缓存。

程序在执行时,会先将内存中的数据加载到共享的L3 Cache中,再加载到每个CPU核心独有的L2 Cache,最后进入到最快的L1 Cache,之后才会被CPU读取。


CPU Cache 的数据结构和读取过程

CPU Cache的数据是从内存中读取来的,它是以一小块一小块的方式读取数据的,不是按照单个数组元素来读取数据。在CPU Cache中,这样一小块一小块的数据称之为Cache Line(缓存块)

CPU读取数据时,无论数据是否放到Cache中,CPU都会先访问Cache,只有当Cache中找不到数据时,才会去访问内存,并把内存中的数据读到Cache中,CPU再从 CPU Cache中读取数据。

内存地址映射到CPU Cache地址有很多种策略,最简单的是直接映射Cache,我们可以通过直接映射来看CPU Cache的数据结构和访问逻辑。它把一个内存的访问地址拆分为组标记、CPU Line索引、偏移量这三种信息,CPU根据这些信息在CPU Cache中找到缓存的数据。对于CPU Cache中的数据结构,则是由索引、有效位、组标记、数据块组成


要想写出让CPU跑得更快的代码就需要写出缓存命中率高的代码,而CPU L1 Cache分为数据缓存和指令缓存,因此需要分别提高它们的缓存命中率:

  • 数据缓存方面:我们在遍历数据时,应该按照内存布局的顺序操作,这是因为CPU Cache是根据CPU Cache Line批量操作数据的,所以顺序操作连续内存数据时,性能能够得到明显的提升。
  • 指令缓存方面:有规律的条件分支语句能够让CPU的分支预测器发挥作用,进一步提高执行的效率。

对于多核CPU系统,线程可能在不同的CPU核心来回切换,这样各个核心的缓存命中率就会降低,所以想提高缓存命中率,可以考虑把线程绑定到某一个CPU核心


1.4 CPU缓存一致性

首先,我们先来了解CPU Cache的数据写入方式–写直达和写回,然后探索缓存一致性问题,最后了解总线嗅探和MESI协议。

快速回顾一下前面的内容

随着时间的推移,CPU和内存之间的访问性能相差越来越大,所以在CPU内部嵌入了CPU Cache(高速缓存),CPU Cache通常分为L1 Cache、L2 Cache、L13Cache,离CPU越近的级别越高,访问速度更快,容量更小。在多核心的CPU中,每个CPU核心都有自己的L1 Cache、L2 Cache,而L3 Cache是所有核心共享的。

CPU Cache是由很多个Cache Line组成的,CPU Line是CPU从内存读取数据的基本单位,而CPU Line是由各种标志(Tag)和数据块(Data Block)组成。

在这里插入图片描述

数据不仅有读操作,还有写操作。那么在数据写入CPU Cache后,内存与Cache中的数据将会不同,所以在这种情况下我们要将Cache和内存中的数据进行同步,但何时进行同步即进行写回,这是一个问题。下满是两种针对数据写回的方法:

写直达: 把数据同时写入内存和Cache中,这是保持内存和Cache一致性最简单的方式。

在数据写入时都会先进行数据是否在CPU Cache中的判断,再进行写入操作:数据若是已经在Cache中了,则先将数据更新到Cache中,再写入到内存中;数据若是不在Cache中,就直接把数据更新到内存中

这个方式的写法简单直接,但因为每次都会进行判断所以会花费大量时间影响性能

在这里插入图片描述

写回: 当发生写操作时,新的数据仅仅被写入Cache Block中,只有当修改过的Cache Block被替换时才需要写到内存中。

减少了数据写回内存的频率,节约了时间,提高了系统性能。

  • 发生写操作时,若数据已经在CPU Cache中,则把数据更新到CPU Cache中,同时标记CPU Cache里的这个Cache Block为脏(Dirty)的,这个脏的标记是指在这个时候,我们CPU Cache中的Cache Block的数据和内存是不一致的,这种情况下不用把数据写到内存中
  • 发生写操作时,若数据不在CPU Cache中,则定位数据对应的Cache Block,检查此时这个Cache Block是否被标记为脏的,是脏的,则将Cache Block中的数据直接写回到内存;若不是脏的,则将当前数据写入到此Cache Block,然后将这个Cache Block标记为脏的
    • 写回方法在数据写入Cache时,只有在缓存不命中,同时数据对应的Cache中的Cache Block为脏标记的情况下才会将数据写到内存中,在缓存命中的情况下,则写入Cache并标记为脏,不写到内存中。

在这里插入图片描述


缓存一致性问题

缓存一致性的问题简单的理解就是在多核CPU里,两个核心同时运行两个线程操作同一个数据,第一个核心通过写回执行了改变该数据的操作,把此时改变了的数据值写入到L1/L2 Cache中,然后将其对应的Block标记为脏的。这个时候的数据是没有被同步到内存中的,此时第二个核心尝试从内存中读取该数据(没有被改变的数据),那么读到的数据与预期是不符的,两个核心的缓存不一致,进而会导致其他执行操作的错误,这就是缓存一致性问题

在这里插入图片描述

为了解决缓存一致性问题,我们需要通过一种机制来同步不同核心里面的缓存数据,而要实现这一机制则需要满足这两个条件:写传播和事务的串形化

写传播: 某个CPU里的Cache数据更新时,必须要传播到其他核心的Cache。

事务的串形化: 某个CPU核心里对数据的操作顺序必须在其他CPU核心看起来顺序是一样的。即假设一个含有4个核心的CPU,这4个核心都操作一个变量,那么当某一个核心对数据进行了更改,其他核心读取到的操作顺序是一样的。A核心对一个变量进行了加操作,同时B核心对这个变量进行了减操作,那么剩余的C、D核心通过写传播收到的数据更改操作顺序不一致,C是先加后减,D是先减后加,那么各个Cache中的数据值是不一致的,把C、D收到数据操作顺序变为一致的过程就是事务的串形化。

  • 为了实现事务的串形化则要做到这两点

    • CPU核心对于Cache中数据的操作,需要同步给其他CPU核心
    • 要引入**“锁”**的概念,如果两个CPU核心里有相同数据的Cache,那么对于这个Cache数据的更新,只有拿到了“锁”才能进行相应的数据更新。
  • 写传播的原则就是当某个CPU核心更新了Cache中的数据,要把该事件广播通知到其他核心。最常见的实现方式就是总线嗅探

    总线嗅探很简单,CPU需要时刻监听总线上的一切活动,但是不管别的核心的Cache是否缓存相同的数据都需要发出一个广播事件,这也会加重总线的负载。

    需要注意的是,总线嗅探只是保证了某个CPU核心的Cache更新数据这个事件能被其他CPU核心知道,但并不能保证事务的串形化

MESI协议

MESI协议基于总线嗅探机制实现了事务串形化,也用状态机制降低了总线带宽压力,实现了CPU缓存一致性。

M:Modified 已修改 即脏标记,代表该Cache Block上的数据已经被更新过,但还没有写到内存中。

I: Invalidated 已失效 表示这个Cache Block里的数据已经失效,不可以读取该状态的数据。

E: Exclusive 独占 该状态下数据只存储在一个CPU核心的Cache中,其他CPU核心的Cache没有该数据。向独占的Cache写入数据不需要通知其他CPU核心,自由写入,不存在缓存一致性问题。

S: Shared 共享 该状态表示该数据在多个CPU核心的Cache中都存在,当我们在进行数据更新时不能直接进行修改,要先对其他CPU核心进行广播一个请求,要求先把其他核心的Cache中对应的Cache Line标记为无效状态再进行数据的更新。

处于已修改或独占状态的Cache Line 修改更新数据不需要发送广播给其他CPU核心。

整个MESI的状态可以用一个有限状态机来表示它的状态流转。MESI是已修改、独占、共享、已实现这四个状态的英文缩写组合,整个MESI状态的变更是根据本地CPU核心的请求,或者来自其他CPU核心通过总线传输过来的请求,从而构造一个流动的状态机。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值