文章目录
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位置的字节。
|
|
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路组相联高速缓存图
6-13,引用地址0x0E34处的字节
6.14,引用地址0x0DD5处的字节
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字节。
如果一级页表是空的,那么二级页表就不会存在,另外,只有一级页表和经常使用的二级页表才是存在在物理内存中的。
5 综合栗子
CSAPP练习题9-4
假设内存是按字节寻址、内存访问1字节的字、虚拟地址VA是14位、物理地址PA是12位、页面大小是64KB、TLB是4路组相联的、L1 cache是物理寻址,直接映射的,16组,每块4字节
最后是一张Core i7地址翻译的简化示意图。