打卡第十二天:07 | Cache与内存:程序放在哪儿?

(2021年11月19日打卡第十二天)



07 | Cache与内存:程序放在哪儿?

学习本节,了解Cache与内存。

1、为什么Cache是解决内存瓶颈的神来之笔?

  • 程序的局部性原理,它告诉我们:CPU 大多数时间在访问相同或者与此相邻的地址。那么,我们立马就可以想到用一块小而快的储存器,放在 CPU 和内存之间,就可以利用程序的局部性原理来缓解 CPU 和内存之间的性能瓶颈。
    这块小而快的储存器就是 Cache,即高速缓存
  • Cache 中存放了内存中的一部分数据,CPU 在访问内存时要先访问 Cache,若 Cache 中有需要的数据就直接从 Cache 中取出,若没有则需要从内存中读取数据,并同时把这块数据放入 Cache 中。
    但是由于程序的局部性原理,在一段时间内,CPU 总是能从 Cache 中读取到自己想要的数据。

2、了解内存的结构和特性

内存条PCB 板上有内存颗粒芯片,主要是用来存放数据的。在PCB班上还有SPD 芯片用于存放内存自身的容量、频率、厂商等信息。还有最显眼的金手指,用于连接数据总线和地址总线,电源等。
其实从专业角度讲,内存应该叫 DRAM,即动态随机存储器。内存储存颗粒芯片中的存储单元是由电容和相关元件做成的,电容存储电荷的多和少分别代表了数字信号 0 和 1。
而随着时间的流逝,电容存在漏电现象,这导致电荷不足,就会让存储单元的数据出错,所以 DRAM 需要周期性刷新,以保持电荷状态。由于DRAM 结构较简单且集成度很高,所以通常用于制造内存条中的储存颗粒芯片。

  • 而作为软件开发人员,从逻辑上我们只需要把内存看成一个巨大的字节数组就可以,而内存地址就是这个数组的下标。

3、Cache带来的一致性问题(以x86 CPU为例)

为了搞清楚这个问题,我们必须先搞清楚 Cache 在硬件层面的结构,下面我画了 x86 CPU 的 Cache 结构图:
x86 CPU的Cache结构图

这是一颗最简单的双核心 CPU,它有三级 Cache,第一级 Cache 是指令和数据分开的,第二级 Cache 是独立于 CPU 核心的,第三级 Cache 是所有 CPU 核心共享的。

Cache 的一致性问题,主要包括这三个方面:

  • 一个 CPU 核心中的指令 Cache 和数据 Cache 的一致性问题。
  • 多个 CPU 核心各自的 2 级 Cache 的一致性问题。
  • CPU 的 3 级 Cache 与设备内存,如 DMA、网卡帧储存,显存之间的一致性问题。

4、理解Cache的MESI协议

  • MESI 协议定义了 4 种基本状态:M、E、S、I,即修改(Modified)、独占(Exclusive)、共享(Shared)和无效(Invalid)。
  • Cache 硬件会监控所有 CPU 上 Cache 的操作,根据相应的操作使得 Cache 里的数据行在上面这些状态之间切换。Cache 硬件通过这些状态的变化,就能安全地控制各 Cache 间、各 Cache 与内存之间的数据一致性了。
  • (引用Spring的留言)MESI分别代表了高速缓存行的四种状态:
    最开始只有一个核读取了A数据,此时状态为E独占,数据是干净的;
    后来另一个核又读取了A数据,此时状态为S共享,数据还是干净的;
    接着其中一个核修改了数据A,此时会向其他核广播数据已被修改,让其他核的数据状态变为I失效,而本核的数据还没回写内存,状态则变为M已修改,等待后续刷新缓存后,数据变回E独占,其他核由于数据已失效,读数据A时需要重新从内存读到高速缓存,此时数据又共享了。

5、动手环节:如何获取内存视图

  • 开启 Cache:
    x86 CPU 上默认是关闭 Cache 的,需要在 CPU 初始化时将其开启。
    在 x86 CPU 上开启 Cache 非常简单,只需要将 CR0 寄存器中 CD、NW 位同时清 0 即可。CD=1 时表示 Cache 关闭,NW=1 时 CPU 不维护内存数据一致性。所以 CD=0、NW=0 的组合才是开启 Cache 的正确方法。
    开启 Cache 只需要用四行汇编代码,代码如下:
mov eax, cr0
;开启 CACHE    
btr eax,29 ;CR0.NW=0
btr eax,30  ;CR0.CD=0
mov cr0, eax
  • 获取内存视图:
    作为系统软件开发人员,关键是要获取哪些物理地址空间是可以读写的内存
    在 x86 平台上使用 BIOS 提供的实模式下中断服务,这个中断服务是 int 15h,但是它需要一些参数,就是在执行 int 15h 之前,对特定寄存器设置一些值,代码如下:
_getmemmap:
  xor ebx,ebx ;ebx设为0
  mov edi,E80MAP_ADR ;edi设为存放输出结果的1MB内的物理内存地址
loop:
  mov eax,0e820h ;eax必须为0e820h
  mov ecx,20 ;输出结果数据项的大小为20字节:8字节内存基地址,8字节内存长度,4字节内存类型
  mov edx,0534d4150h ;edx必须为0534d4150h
  int 15h ;执行中断
  jc error ;如果flags寄存器的CF位置1,则表示出错
  add edi,20;更新下一次输出结果的地址
  cmp ebx,0 ;如ebx为0,则表示循环迭代结束
  jne loop  ;还有结果项,继续迭代
    ret
error:;出错处理

上面的代码是在迭代中执行中断,每次中断都输出一个 20 字节大小数据项,最后会形成一个该数据项(结构体)的数组,可以用 C 语言结构表示,如下:

#define RAM_USABLE 1 //可用内存
#define RAM_RESERV 2 //保留内存不可使用
#define RAM_ACPIREC 3 //ACPI表相关的
#define RAM_ACPINVS 4 //ACPI NVS空间
#define RAM_AREACON 5 //包含坏内存
typedef struct s_e820{
    u64_t saddr;    /* 内存开始地址 */
    u64_t lsize;    /* 内存大小 */
    u32_t type;    /* 内存类型 */
}e820map_t;

6、程序的基石:硬件(3讲)的这三节课的知识导图。

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值