文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

我的内存怎么不够用了?

2024-12-03 09:12

关注

为什么内存不够用?

要理解一个技术,就必须理解它为何而存在。总体来说,虚拟化技术是为了解决内存不够用的问题,那么内存为何不够用呢?

主要是因为程序越来越复杂。比如说我现在给你录音的机器上就有 200 个进程,目前内存的消耗是 21G,我的内存是 64G 的,但是多开一些程序还是会被占满。另外,如果一个程序需要使用大的内存,比如 1T,是不是应该报错?如果报错,那么程序就会不好写,程序员必须小心翼翼地处理内存的使用,避免超过允许的内存使用阈值。以上提到的这些都是需要解决的问题,也是虚拟化技术存在的价值和意义。

那么如何来解决这些问题呢?历史上有过不少的解决方案,但最终沉淀下的是虚拟化技术。接下来我为你介绍一种历史上存在过的 Swap 技术以及虚拟化技术。

交换(Swap)技术

Swap 技术允许一部分进程使用内存,不使用内存的进程数据先保存在磁盘上。注意,这里提到的数据,是完整的进程数据,包括正文段(程序指令)、数据段、堆栈段等。轮到某个进程执行的时候,尝试为这个进程在内存中找到一块空闲的区域。如果空间不足,就考虑把没有在执行的进程交换(Swap)到磁盘上,把空间腾挪出来给需要的进程。

 

上图中,内存被拆分成多个区域。内核作为一个程序也需要自己的内存。另外每个进程独立得到一个空间——我们称为地址空间(Address Space)。你可以认为地址空间是一块连续分配的内存块。每个进程在不同地址空间中工作,构成了一个原始的虚拟化技术。

比如:当进程 A 想访问地址 100 的时候,实际上访问的地址是基于地址空间本身位置(首字节地址)计算出来的。另外,当进程 A 执行时,CPU 中会保存它地址空间的开始位置和结束位置,当它想访问超过地址空间容量的地址时,CPU 会检查然后报错。

上图描述的这种方法,是一种比较原始的虚拟化技术,进程使用的是基于地址空间的虚拟地址。但是这种方案有很多明显的缺陷,比如:

  1. 碎片问题:上图中我们看到进程来回分配、回收交换,内存之间会产生很多缝隙。经过反反复复使用,内存的情况会变得十分复杂,导致整体性能下降。
  2. 频繁切换问题:如果进程过多,内存较小,会频繁触发交换。

首先重新 Review 下我们的设计目标。

  1. 隔离:每个应用有自己的地址空间,互不影响。
  2. 性能:高频使用的数据保留在内存中、低频使用的数据持久化到磁盘上。
  3. 程序好写(降低程序员心智负担):让程序员不用关心底层设施。

现阶段,Swap 技术已经初步解决了问题 1。

关于问题 2,Swap 技术在性能上存在着碎片、频繁切换等明显劣势。

关于问题3, 使用 Swap 技术,程序员需要清楚地知道自己的应用用多少内存,并且小心翼翼地使用内存,避免需要重新申请,或者研发不断扩容的算法——这让程序心智负担较大。

经过以上分析,需要更好的解决方案,就是我们接下来要学习的虚拟化技术。

虚拟内存

虚拟化技术中,操作系统设计了虚拟内存(理论上可以无限大的空间),受限于 CPU 的处理能力,通常 64bit CPU,就是 264 个地址。

 

虚拟化技术中,应用使用的是虚拟内存,操作系统管理虚拟内存和真实内存之间的映射。操作系统将虚拟内存分成整齐小块,每个小块称为一个页(Page)。之所以这样做,原因主要有以下两个方面。

一方面应用使用内存是以页为单位,整齐的页能够避免内存碎片问题。

另一方面,每个应用都有高频使用的数据和低频使用的数据。这样做,操作系统就不必从应用角度去思考哪个进程是高频的,仅需思考哪些页被高频使用、哪些页被低频使用。如果是低频使用,就将它们保存到硬盘上;如果是高频使用,就让它们保留在真实内存中。

如果一个应用需要非常大的内存,应用申请的是虚拟内存中的很多个页,真实内存不一定需要够用。

页(Page)和页表

接下来,我们详细讨论下这个设计。操作系统将虚拟内存分块,每个小块称为一个页(Page);真实内存也需要分块,每个小块我们称为一个 Frame。Page 到 Frame 的映射,需要一种叫作页表的结构。

上图展示了 Page、Frame 和页表 (PageTable)三者之间的关系。Page 大小和 Frame 大小通常相等,页表中记录的某个 Page 对应的 Frame 编号。页表也需要存储空间,比如虚拟内存大小为 10G, Page 大小是 4K,那么需要 10G/4K = 2621440 个条目。如果每个条目是 64bit,那么一共需要 20480K = 20M 页表。操作系统在内存中划分出小块区域给页表,并负责维护页表。

 

页表维护了虚拟地址到真实地址的映射。每次程序使用内存时,需要把虚拟内存地址换算成物理内存地址,换算过程分为以下 3 个步骤:

  1. 通过虚拟地址计算 Page 编号;
  2. 查页表,根据 Page 编号,找到 Frame 编号;
  3. 将虚拟地址换算成物理地址。

下面我通过一个例子给你讲解上面这个换算的过程:如果页大小是 4K,假设程序要访问地址:100,000。那么计算过程如下。

页编号(Page Number) = 100,000/4096 = 24 余1619。24 是页编号,1619 是地址偏移量(Offset)。

查询页表,得到 24 关联的 Frame 编号(假设查到 Frame 编号 = 10)。

换算:通常 Frame 和 Page 大小相等,替换 Page Number 为 Frame Number 物理地址 = 4096 * 10 + 1619 = 42579。

MMU

上面的过程发生在 CPU 中一个小型的设备——内存管理单元(Memory Management Unit, MMU)中。如下图所示:

 

当 CPU 需要执行一条指令时,如果指令中涉及内存读写操作,CPU 会把虚拟地址给 MMU,MMU 自动完成虚拟地址到真实地址的计算;然后,MMU 连接了地址总线,帮助 CPU 操作真实地址。

这样的设计,就不需要在编写应用程序的时候担心虚拟地址到物理地址映射的问题。我们把全部难题都丢给了操作系统——操作系统要确定MMU 可以读懂自己的页表格式。所以,操作系统的设计者要看 MMU 的说明书完成工作。

难点在于不同 CPU 的 MMU 可能是不同的,因此这里会遇到很多跨平台的问题。解决跨平台问题不但有繁重的工作量,更需要高超的编程技巧,Unix 最初期的移植性(跨平台)是 C 语言作者丹尼斯·里奇实现的。

MMU 需要查询页表(这是内存操作),而 CPU 执行一条指令通过 MMU 获取内存数据,难道可以容忍在执行一条指令的过程中,发生多次内存读取(查询)操作?难道一次普通的读取操作,还要附加几次查询页表的开销吗?当然不是,这里还有一些高速缓存的设计,这部分后面还可以继续讨论。

页表条目

上面我们笼统介绍了页表将 Page 映射到 Frame。那么,页表中的每一项(页表条目)长什么样子呢?下图是一个页表格式的一个演示。

 

页表条目本身的编号可以不存在页表中,而是通过偏移量计算。比如地址 100,000 的编号,可以用 100,000 除以页大小确定。

Frame Number(Frame 编号),这个是真实内存的位置。用 Frame 编号乘以页大小,就可以得到 Frame 的基地址。

在 64bit 的系统中,考虑到 Absent、Protection 等字段需要占用一定的位,因此不能将 64bit 都用来描述真实地址。但是 64bit 可以寻址的空间已经远远超过了 EB 的级别(1EB = 220TB),这已经足够了。在真实世界,我们还造不出这么大的内存呢。

大页面问题

最后,我们讨论一下大页面的问题。假设有一个应用,初始化后需要 12M 内存,操作系统页大小是 4K。那么应该如何设计呢?

为了简化模型,下图中,假设这个应用只有 3 个区域(3 个段)——正文段(程序)、数据段(常量、全局变量)、堆栈段。一开始我们 3 个段都分配了 4M 的空间。随着程序执行,堆栈段的空间会继续增加,上不封顶。

上图中,进程内部需要一个页表存储进程的数据。如果进程的内存上不封顶,那么页表有多少个条目合适呢?进程分配多少空间合适呢?如果页表大小为 1024 个条目,那么可以支持 1024*4K = 4M 空间。按照这个计算,如果进程需要 1G 空间,则需要 256K 个条目。我们预先为进程分配这 256K 个条目吗?创建一个进程就划分这么多条目是不是成本太高了?

 

为了减少条目的创建,可以考虑进程内部用一个更大的页表(比如 4M),操作系统继续用 4K 的页表。这就形成了一个二级页表的结构,如下图所示:

 

这样 MMU 会先查询 1 级页表,再查询 2 级页表。在这个模型下,进程如果需要 1G 空间,也只需要 1024 个条目。比如 1 级页编号是 2, 那么对应 2 级页表中 [2* 1024, 3*1024-1] 的部分条目。而访问一个地址,需要同时给出一级页编号和二级页编号。整个地址,还可以用 64bit 组装,如下图所示:

MMU 根据 1 级编号找到 1 级页表条目,1 级页表条目中记录了对应 2 级页表的位置。然后 MMU 再查询 2 级页表,找到 Frame。最后通过地址偏移量和 Frame 编号计算最终的物理地址。这种设计是一个递归的过程,因此还可增加 3 级、4 级……每增加 1 级,对空间的利用都会提高——当然也会带来一定的开销。这对于大应用非常划算,比如需要 1T 空间,那么使用 2 级页表,页表的空间就节省得多了。而且,这种多级页表,顶级页表在进程中可以先只创建需要用到的部分,就这个例子而言,一开始只需要 3 个条目,从 256K 个条目到 3 个,这就大大减少了进程创建的成本。

 

来源:JerryCodes内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯