内存的虚拟化技术

交换(Swap)技术
1,Swap 技术即正在执行进程使用内存,没有在执行中的进程数据先保存在磁盘上。
1)进程数据,包括正文段(程序指令)、数据段、堆栈段等。
2)轮到某个进程执行的时候,尝试为这个进程在内存中找到一块空闲的区域。
3)如果空间不足,将没有在执行的进程数据交换(Swap)到磁盘上,把空间腾出来给需要的进程。

2,将内存拆分成多个区域,地址空间是一块连续分配的内存块,每个进程得到独立的地址空间,构成一个原始的虚拟化技术,但存在明显缺陷。
1)每个进程在不同地址空间中工作,进程使用的是基于地址空间的虚拟地址。
2)进程实际上访问的地址是基于地址空间首地址计算出来的偏移。
3)CPU 会保存进程地址空间的开始位置和结束位置,当进程想访问超过地址空间容量的地址时,CPU 会检查然后报错。
4)碎片问题,当进程来回回收交换、分配,内存之间会产生很多缝隙,经过反反复复使用,内存的情况会变得十分复杂,导致整体性能下降;
5)频繁切换问题,如果进程过多,内存较小,会频繁触发交换。

3,虚拟化技术中,操作系统设计了虚拟内存(理论上可以无限大的空间),应用使用虚拟内存,操作系统管理虚拟内存和真实内存之间的映射。
1)操作系统将虚拟内存分成整齐小块,每个小块称为一个页(Page)。
2)应用使用内存以页为单位,整齐的页能够避免内存碎片问题;
3)每个应用有高频使用的数据和低频使用的数据,操作系统不需要考虑进程的高频或低频使用,只需要考虑页的高频或低频使用;
4)低频使用的页,保存到硬盘上,高频使用的页,保留在真实内存中。
5)如果应用需要非常大的内存,可申请虚拟内存多个页,不需要真实内存够用。
6)虚拟页大小与物理页大小相等。
7)受限于 CPU 的处理能力,通常 64bit CPU,就是 264 个地址。

4,应用使用内存时,需要将虚拟内存地址换算成物理内存地址。
1)通过虚拟地址计算虚拟页编号,查页表,根据虚拟页编号找到对应物理页编号,将虚拟地址换算成物理地址。
2)页表维护了虚拟地址到真实地址的映射。

内存管理单元(Memory Management Unit, MMU)
1,当 CPU 执行一条指令,指令中涉及内存读写操作,CPU 会将虚拟地址给 到MMU,MMU 自动完成虚拟地址到真实地址的计算;然后,MMU 连接地址总线,帮助 CPU 操作真实地址。
1)应用程序设计不需要担心虚拟地址到物理地址映射的问题。
2)操作系统确定MMU 的页表格式,不同 CPU 的 MMU 可能是不同的,可能会有跨平台的问题。
3)MMU 查询页表是内存操作。

2,虚拟地址由页号和偏移量组成,物理地址也由Frame Number和偏移量组成, CPU 中完成虚拟地址到物理地址转换的内存管理单元(Memory Management Unit (MMU)) 。
1)程序执行时,指令中的地址是虚拟地址,虚拟地址通过 MMU查询页表,计算出对应的 Frame Number,偏移量不变,组装成真实地址。
2)MMU 通过地址总线直接访问内存。

3,MMU 承担了虚拟地址到物理地址的转换以及 CPU 对内存的操作。
1)结构上 MMU 在 CPU 内部,并且直接和地址总线连接,因此 MMU 承担了 CPU 和内存之间的代理。
2)对操作系统而言,MMU 是一类设备,有多种型号,操作系统需要根据不同型号使用 MMU。

转置检测缓冲区(Translation Lookaside Buffer,TLB)
1,在 MMU 中有一个微型的设备,叫转置检测缓冲区(Translation Lookaside Buffer,TLB),缓存的设计,通常是一张表,所以 TLB 也称作快表。
1)指令执行的速度非常快, MMU 需要从内存中查询页表,最快的内存查询页需要从 CPU 的缓存中读取,假设缓存有 95% 的命中率,读取到了 L2 缓存,每次操作需要几个 CPU 周期,而CPU的每次指令周期也可能发生内存操作,则要求内存操作要快,尤其Page Number 到 Frame Number 的映射,最好不到 0.2 个 CPU 周期,不会因为地址换算而增加指令的 CPU 周期。
2)TLB 中最主要的信息就是 Page Number到 Frame Number 的映射关系。
3)最简单的表达就是一个二维表格,每一行是一个 Page Number 和一个 Frame Number。每一行称为一个缓存行(Cache Line),或者缓存条目(Entry)。

2,TLB 根据输入的 Page Number,找到 Frame Number,TLB 是硬件实现,因此速度很快。
1)用户的局部程序,如果反复利用相同的内存地址,比如 for 循环反复利用循环变量,TLB几十行的缓存行命中率会非常高。
2)现在的多核 CPU,每个核心都有自己的 TLB,而且 TLB 还进行了类似 CPU 缓存的分级策略,L1级的TLB,L2级的TLB,绝大多数的页表查询都可以用 TLB 实现了。
3)如果 Page Number 在 TLB 中没有找到,称为TLB 失效(Miss)。

3,软失效(Soft Miss), Frame 在内存中,但 TLB 缓存中没有,需要刷新 TLB 缓存。
1)如果 TLB 缓存已经满了,需要选择一个已经存在的缓存条目进行覆盖。
2)选择哪个条目进行覆盖,称为缓存置换。
3)缓存置换时,保留高频使用的数据,替换低频使用的数据,如常用的 LRU(Least Recently Used),每次置换最早使用的条目。

4,硬失效(Hard Miss), Frame 不在内存中,需要从磁盘加载。
1)操作系统需要触发缺页中断,原有读取内存的线程被休眠,中断响应程序从磁盘读取对应的 Frame 到内存中,读取完成后,再次触发中断通知更新 TLB,并且唤醒被休眠的线程去排队。
2)线程不可能从休眠态不排队就进入执行态,因此 Hard Miss 是相对耗时的。

TLB缓存行设计
1,无论是软失效、还是硬失效,都会带来性能损失,缓存的设计,就非常重要了,缓存行(Cache Line)的缓存有 3 种映射方案:
1)全相联映射(Fully Associative Mapping);
2)直接映射(Direct Mapping);
3)n 路组相联映射(n-way Set-Associative Mapping)。

2,相联(Associative),即缓存条目和缓存数据之间的映射范围。
1)如果是全相联,一个数据,可能在任何条目。
2)如果是组相联(Set-Associative),一个数据,只能在一部分缓存条目中出现(比如前 4 个条目)。

3,全相联映射(Fully Associative Mapping),一个Frame,可能在任何缓存行中,给定一个具体的 Page Number查找 Frame,软件设计需要遍历整个缓存。
1)如果缓存条目少的情况,硬件电路可以实现并行查找过程。
2)如果条目过多,几百个上千个,硬件查询速度会下降,所以全相联映射,有着明显性能上的缺陷。

4,直接映射(Direct Mapping)类似哈希函数的形式,给定一个内存地址,通过类似于哈希函数计算它在哪一个缓存条目,但不能基于直接映射实现LRU 缓存。
1)确定的虚拟地址,它所在的条目也是确定的,不支持各种缓存置换算法,因此 TLB Miss 肯定会更高。

5,n 路组相联映射(n-way Set-Associative Mapping),组相联映射有点像哈希表的开放寻址法。
1)组相联映射允许一个虚拟页号(Page Number)映射到固定数量的 n 个位置,一个 Page Number 可以在 n 个位置出现,解决了 LRU 算法的问题。
2)新地址需要置换进来时,可以从 n 个位置中选择更新时间最早的条目置换出去,n 设置为多少,需要收集数据实际验证。
3)核心是实现类似 LRU 的算法,保留高频使用的缓存。
4)既解决了直接映射的缓存命中率问题,又解决了全相联映射的性能问题。

大内存分页
1,采用大内存分页(Large Page 或 Huge Page),如果操作系统提供大小为 4M 的页,直接减少了 1024 倍的页数,大大提高了 TLB 的查询性能。
1)应用(进程)对内存的需求比较大,而默认分页 4K 比较小,页表过多,页表对应关系也多,缓存行命中的概率更低。
2)使用多级页表,每个一级页表项关联一个二级页表, MMU 需要先检查一级页表,再检查二级页表,会给 MMU 带来一定的负担, MMU可以利用 TLB缓存,但解决不了页表太多的问题,多级页表最终还是需要查询大小为 4K 的页。
3)Linux 内核 2.6 版之后就开始提供大内存分页(HugeTable),默认是不开启的。
4)应用需要使用大内存分页,可以考虑用下面的语句开启它:sudo sysctl -w vm.nr_hugepages=2048。

2,应用对内存需求较大时,考虑开启大内存分页,比如一个搜索引擎,需要大量在内存中的索引。
3,有时应用对内存的需求是隐性的,如对抗高并发访问的机器,当高并发请求时,应用对内存的需求大大提高,虽然每个并发请求需要的内存不大, 但总量上去了,需求总量也会随之提高,这种情况下,也可以考虑开启大内存分页。

缓存置换算法
1,缓存置换场景,如发生缺页中断后,操作系统需要将磁盘的页导入内存,已在存在内存中的页需要置换出去;CDN服务器提高访问速度,部分Web资源在内存中,部分在磁盘上,CPU缓存每写入一个条目,旧的条目需要被覆盖。

2,把最近没有使用的数据置换出去(Not Recently Used),从概率上最近没有使用的数据未来使用的概率比最近经常使用的数据低。

3,最近最少使用(Least Recently Used, LRU)算法,置换最久没有使用的数据。
1)和 NRU 相比,LRU 考虑的是一个时间范围内的数据,对数据的参考范围更大,比 NRU 考虑更周密,但实现成本更高。
2)CPU 缓存利用 LUR 算法将空间留给频繁使用的内存数据,淘汰使用频率较低的内存数据,用双向链表维护缓存条目。
3)如果链表中某个缓存条目被使用到,将这个条目重新移动到表头,如果要置换缓存条目出去,直接从双线链表尾部删除一个条目。
4)LRU 缓存还需要提供查询能力,同时维护一个链表和一个哈希表,在缓存访问量非常大的情况下开销会比较高。
5)LRU页面置换算法需要维护一个大的链表来存储所有页,且经常删除大量的页面(置换缓存),将大量的页面移动到链表头部,对页面置换高性能需求的场景来说,是不可以接受的。
6)CPU 的缓存,采用多路组相联设计,比如 8-way 设计,需要在 8 个地址中快速找到最久未使用的数据,不可能再去内存中建立一个链表来实现。

4,LRU老化算法,在LRU页面置换算法中,增加定时器功能,累计页表的访问次数,并将累计值右移。
1)比如8位累计数,定时器右移累计数,如果页表访问则在最高位置1,如果无访问仅右移累计数,有访问操作累计值上升,没有访问操作累计值逐渐减少。
2)如果累计值不减少,有内存数据过去常常被用到,但是现在已经有很长一段时间没有被用到了,这种情况下数据并不会置换出去,所以需要将累计值右移。
3)计算累计值的过程可以由硬件实现,可以最大程度提升性能。
4)相比写入操作,查询耗时相对较少, CPU 缓存的存在,不需要去内存中查找数据,而是在缓存中进行。
5)发生缺页中断时,也不需要追求绝对的精确,可以在部分页中找到一个相对累计值较小的页面进行置换。
6)LRU算法不是所有操作都能硬件直接支持,总有一部分需要软件实现,还是有较多的时间开销。

5,是否采用 LRU,要看所在场景的性能要求,有没有足够的优化措施(比如硬件提速);还要看最终的结果能否达到期望的命中率和期望的使用延迟。
1)也有不适用缓存置换的场景,比如读取大型文件,很难建立有效的缓存。
2)有些场景下缓存使用频次最低的数据置换频次最高的数据,效率更高。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值