linux虚拟内存、存储器层次结构、缓存

1. 存储器

什么是存储器?存储器就是存放数据的容器,常见的有RAM、ROM等。有个问题,为什么它们叫随机访问存储器?是因为访问一个数据的时间与数据在存储器的位置无关。常见的cache就是用的SRAM,内存用的是DRAM,它们都是断电丢失的,断电不丢失的叫ROM,比如存放我们装机用的BIOS,又或者基于EEPROM的SSD硬盘。

这些存储器的区别是什么?为什么分这么多?我们都知道CPU处理指令的速度是非常快的,访问寄存器0个周期就可以完成,访问缓存需要几个到几十个时钟周期就能完成,但是访问内存的话就需要几百个时钟周期来完成,而访问磁盘的话就需要几千万个周期,这就造成了处理差异,即使你CPU运算再快,需要的数据没有送到也无济于事啊!所以就有了计算机存储器层次结构,如下图。
存储器层次结构
另外,现在多核CPU的话,每个核内有单独的L1 cache(L1 data cache和L1 instruction cache)、L2 cache以及所有核共享L3 cache。

2. DRAM寻址

假如有一个 16X8 的内存(16表示有16个存储单元,8表示每个存储单元可以存放8个bit),在我们人脑中,我们一般将内存看成是一个连续的数组,如图所示(假设CPU每次取一个字节),假设我们取13位置的字节。
假想的内存模型

实际上,内存控制器先将行地址发到DRAM,然后将整行复制到行缓冲区,接下来,控制器发送列地址,DRAM根据列地址从行缓冲区复制选中的的8位,将它们送回给控制器。这么做的好处是,按线性数组的话需要4个引脚(0000 ~ 1111),但是二维阵列的话只需要2个引脚(00 ~ 11),但这样的话就需要分两次发送地址,增加了访问时间。

3. cache缓存

我们以Core i7的L1 cache为例,介绍一下缓存的工作原理(先不考虑虚拟地址)。
先了解一下下列英文单词释义:

PPN:Physical Page Number物理页号
PPO: Physical Page Offset物理页偏移
CT: Cache Tag缓存标记
CI: Cache Index缓存组索引
CO: Cache Offset缓存块偏移

高速缓存是通过一个三元组(CT,CI,CO)来索引的,如图所示,我们先根据CI找到组索引,然后根据标记CT来找到所在的行,最后根据块内偏移CO找到对应字节,这种情况称为cache hit(缓存命中),否则为cache miss(缓存不命中)。注意,每行都有一个状态位state,只有状态位标志位有效该行才有意义。
缓存都是以块为单位工作的,数据在各级缓存之间都是来回复制块个字节。另外,组内的行数是硬件做好的,不是由地址信息决定的。在Core i7中,L1 cache是由8行组相联的。

直接映射高速缓存:每个组织有一行
组相联高速缓存:每个组有若干行
全相联高速缓存:只有一个组,组内有若干行

高速缓存
顺便说一下,在我们64位的系统中,虚拟地址是由64位组成的,但在目前的实现中,这些地址的高16位全部为0,所以一个虚拟地址的有效位是48位,能够访问的内存空间是256TB。实际的物理地址会比虚拟地址大一点,也就是52位(CT+CI+CO=40+6+6),后面我们会讲它们是如何转换的。

缓存不命中—替换策略
1、随机替换:随机选择一行进行替换
2、LRU(Least-Recently-Used):替换最后一次访问时间最久远的那一次
3、LFU(Least-Frequently-Used):替换最近使用次数最少的一行

之前是读,写的情况就复杂了。对于写命中,分为两种,一种是直写:立即将缓存块写回到低一层的缓存中;另一种是写回:只有替换算法要驱逐这个更新过的块时才写到低一层缓存中。直接总是会占用IO总线,写回会增加复杂性(比如,对于L3共享缓存,一个核还没有写更新,另一个核就要读取,导致了不一致的问题)。对于写不命中,也是分为两种,一种是写分配:加载低一层的的块到高速缓存上,然后更新;另一种是写不分配:直接写到低一层的缓存中(不命中的话继续往下一层搜索)。

举个栗子,以CSAPP练习题6.13 ~ 6.15为例:

假设内存是字节寻址的,且访问的是1字节的字,地址长度设定为13位,下图是某个2路组相联高速缓存图
2路组相联高速缓存
6-13,引用地址0x0E34处的字节
6.13 0x0E34
6.14,引用地址0x0DD5处的字节
6.14 0x0DD5
6.15,引用地址0x1FE4处的字节
6.15 0x1FE4
看到这里大家应该对缓存的工作原理不默笙了吧,继续往下看吧!

4. 虚拟内存VM

先看一下linux的虚拟地址空间。注:代码段总是从0x00400000,内核虚拟内存对用户不可见,且对所有进程都一样。
进程虚拟地址空间

简单的来说,一个运行着的程序就是一个进程。在我们现在的电脑上,可以同时运行很多程序,但这些程序却互不干扰,好像是独占的使用物理内存,这全都归功于虚拟内存。操作系统给每个进程提供了大小相同、地址空间一致的虚拟内存(比如说4G),这些虚拟内存是在磁盘上运行的,只有被使用到的虚拟地址空间才会被映射到物理内存上,一般来说普通的程序根本用不到4G内存,所以映射到物理内存上的空间也少,这样,一个物理内存地址空间可以让许多虚拟内存映射,所以电脑可以让很多程序同时运行。

1、虚拟内存将物理内存看做是是存储在磁盘上地址空间的高速缓存,在主存中只保存活动区域;
2、虚拟内存为每个进程提供一致的地址空间,它们都有相同的内存分区;
3、虚拟内存保护了各个进程的地址空间不被破坏;

先将本节用到的英文单词认识一下:

VA: Virtual Address虚拟地址
PA: Physical Address物理地址
MMU: Memory Management Unit内存管理单元
VP: Virtual Page虚拟页
PP: Physical Page物理页
PT: Page Table页表
PTE: Page Table Entry页表条目
PPN: Physical Page Number物理页号
PPO: Physical Page Offset物理页面偏移
VPN: Virtual Page Number虚拟页号
VPO: Virtual Page Offset虚拟页面偏移
TLB: Translation Lookaside Buffer快表
4.1 假设没有cache

现代CPU都是生成一个虚拟地址来访问主存的。另外,按第 2 小节来假设我们物理内存是数组形式的,如下图所示,虚拟地址0x1234被地址翻译单元MMU翻译为了128,这样主存控制器从128的位置取出字word传回给CPU。
假想的虚拟地址系统
那么虚拟地址到物理地址是怎样映射的呢?
同前面说的一样,各级缓存是以块为工作单位的,那磁盘上的虚拟内存也不例外,它们被分割成块,以此来作为数据传输单元,只不过这些块被叫成了页,页的大小通常为4KB,有的是4MB。虚拟内存的被称为了虚拟页VP,物理内存上的被称为物理页PP。
地址映射
已缓存的表示已经缓存到物理内存上;未缓存的表示还没有缓存到物理内存上;未分配的表示虚拟内存上还没有使用到的页。
前面说过,访问磁盘的时间特别大,所以发生不命中的开销也很大,故DRAM采用的是写回而不是直写,同时为了保证任何虚拟页都能放到任何物理页,DRAM采用的是全相联。

那么操作系统是如何知道一个虚拟页VP是否缓存到了一个物理页PP中?并且怎么知道虚拟页缓存在哪个物理页中?页表PT和页表条目PTE来帮助操作系统完成这个功能。页表PT是放在物理内存中的数据结构,虚拟地址和物理地址转换时都会读取页表。而页表条目是是每个虚拟页在页表中都会有一个固定偏移量。
页表条目
如图所示,假设虚拟地址大小为15位,每个页的大小为4KB,那么需要的页表条目为215/212=8,假如物理内存为4KB*4=16KB。页表有效位为1,后面的地址表示物理页号;有效位为0,后面的地址表示磁盘地址;若磁盘地址为NULL,则表示当前虚拟页还未被分配。这样,当操作系统要查询VP3中的字时,它会根据虚拟地址索引来定位到PTE3,发现还未缓存(缺页),那么就会采用某种替换策略换掉当前在物理内存上缓存的虚拟页,并会更新页表;当定位到PTE2时,发现有效位为1,表明物理内存已经缓存了这个页,它会构造出要访问内容的物理地址。(后面还会详细介绍)

还要强调一下,操作系统为每个进程提供了一个独立的页表存放在物理内存上,这样每个进程都有自己独立的内存映射关系。内核是可以看到所有的页表的,如果进程i和进程j都往同一个物理页上映射不同的虚拟页就会发生冲突,内核会解决这个冲突;另外,进程i和进程j也会共享物理内存上的同一个物理页(比如linux中的.so文件和window的.dll文件),避免浪费内存。
共享页面

4.2 地址翻译

CPU中有一个控制寄存器,页表基址寄存器(PTBR)指向当前页表。如图所示,前面说过虚拟页号表示虚拟页在页表的偏移地址,这样在查询时如果有效位为1则表明已缓存到物理内存中,那么会直接取出存储的物理页号,这样和物理页偏移量合起来构成了物理地址(虚拟页和物理页都采用页面大小一样的4KB,故偏移量都一样),这样就完成了虚拟地址到物理地址的翻译。
地址翻译
下面分别讲述一下命中与缺页的情况
页面命中:
页面命中

1、处理器生成一个虚拟地址给MMU;
2、MMU根据虚拟地址的虚拟页号查询在物理内存中的页表,物理内存返回页表条目给MMU;
3、MMU生成实际的物理地址;
4、物理内存根据物理地址返回给CPU数据;

缺页:
缺页

1、处理器生成一个虚拟地址给MMU;
2、MMU根据虚拟地址的虚拟页号查询在物理内存中的页表,物理内存返回页表条目给MMU;
3、但这个页表条目有效位为0,表明未缓存,因此MMU触发一个异常;
4、缺页异常处理程序选择一个物理内存中的牺牲页,若这个页被修改过,那么会写回到虚拟内存;然后将新的页面加载到物理内存中,并更新页表;
5、异常处理程序返回到原来中断的地方继续执行步骤2和3,此时缺页变为命中,MMU生成物理地址;
6、物理内存根据物理地址返回给CPU数据;

4.2 结合cache

前面提过,CPU产生的是虚拟地址,并且CPU中寄存器是先访问L1 cache的,那么访问的时候是使用虚拟地址还是物理地址?答案是基本上使用的都是物理地址,为什么呢?因为cache空间很小很宝贵,物理内存缓存在cache的东西可以被多个进程共享,而且高速缓存只缓存数据,并不判断地址的合法性,地址合法性应该在地址翻译阶段完成。
下图是结合虚拟内存和高速缓存的物理寻址过程,页表条目也是物理内存上的数据,因此它也可以缓存在高速缓存中。

虚拟内存和物理寻址的结合

cache都比较小,所以每个空间都很宝贵,MMU在查询页表条目PTE的时候如果没命中,就会继续往下一层查找,这样会造成很多不必要的开销,因此提出来一个快表的概念(TLB)。TLB是一个缓存页表条目的cache,按上图命中所示,CPU生成一个虚拟地址,MMU根据虚拟地址去TLB中查找PTE,命中则返回PTE,然后提取PA送给L1 cache;如不命中则去L1 cache中查找PTE,然后更新TLB。

4.3 多级页表

最后有一个问题,一个32位的系统,每个页面是4KB,那么就有232/212=1M个页表放在物理内存上,64位的更甚,这显然是不合理的,所以出现了多级页表。如下图所示是一个二级页表,每个页表条目为4字节。
二级页表
如果一级页表是空的,那么二级页表就不会存在,另外,只有一级页表和经常使用的二级页表才是存在在物理内存中的。
K级页表地址翻译

5 综合栗子

CSAPP练习题9-4
假设内存是按字节寻址、内存访问1字节的字、虚拟地址VA是14位、物理地址PA是12位、页面大小是64KB、TLB是4路组相联的、L1 cache是物理寻址,直接映射的,16组,每块4字节
求解地址位数
TLB_页表_缓存
最后是一张Core i7地址翻译的简化示意图。
corei7地址翻译
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值