【内存管理】【前世今生】

我们在学习一个新鲜事务之前, 最好是先搞清楚这个事务发展的来龙去脉。这样有助于增加对该事务的好感,进而有信心去探索它。

1. 内存管理演变过程

我们在学习linux 内存管理也是如此。今天我们就先八卦一下它的演变史。

我们先设想一下,让你发明一个计算机。我们的程序该如何才能跑起来呢。
什么是程序?

  • 教条一点的说法就是: 程序是指一组指令的集合,用于指导计算机执行特定任务或解决特定问题
  • 简单的说: 就是告诉cpu 第一步, 第二步 要去做什么。那是不是要去做A这个事情,可以拆分成很多步。 步骤的集合就是我们的程序A。 同理 程序B 、C、D。

刚开始条件有限, 我们可以把 程序A 、B、C、D 分别写在一个纸条上。 放在一个 纸篓里面(硬盘)。 当我们要运行 程序A , 时我们从纸篓中(磁盘)中找到对应的纸条。 将纸条的内容 读出来放置到 cpu 可以访问到的地方(内存)。 然后cpu 来一条条去执行 程序A 对应的步骤。

伴随着时代的变迁,科技的进步。已经不再使用 纸张来记录我们的程序了。 而是将我们 程序的二进制保存在我们的磁盘中。 cpu 执行时将磁盘中的二进制加载到我们的 内存中。

1.1 动态分区法

  1. 假设我们的物理内存只有 30MB
  2. 程序A 需要 6MB
  3. 程序B 需要 12MB
  4. 程序C 需要 10MB
  5. 程序D 需要 5MB

在这里插入图片描述

在计算机发展的早期, 进程是直接运行在物理内存中的

  1. 情况一:
    1. 进程A 进程C 进程B 同时运行。
    2. 如果此时我们要运行 进程 D, 需要 5MB大小的物理空间。那我们该如何处理? 此时物理内存还剩余 2MB 空间。不足以运行进程D。
  2. 情况二:
    1. 我们只好将进程A 6MB, 交换到 我们的磁盘中, 将我们的 进程D 加载进来。
    2. 此时 进程 D 和 进程 C 之间就出现了 1MB的空洞。
    3. 如果此时 我们又要 执行进程 A, 需要 6MB 物理空间。同时进程D 也需要执行。那该如何处理?
  3. 情况三:
    1. 我们可以把 C 10MB 先交换到 磁盘中。此时再将我们先前交换出去的进程A 6MB , 在加载到我们的物理内存中执行。
    2. 此时发现 我们存在如图所示的三个空洞。
      1. 进程D 和 进程A 之间有一个 1MB的空洞。
      2. 进程A 和 进程B 之间 有一个 4MB 的空洞
      3. 而物理内存最上面还有一个 2MB 的空洞。
    3. 上面这个过程就是 碎片产生的原因。
  • 由于早期, 程序是直接运行在物理内存中的。 而且程序之间没有保护。 很容易篡改其他进程中的数据,导致出现异常。
  • 并且 随着运行时间的变长。程序会不断的在内存和磁盘中交换。 而且每次交换后在内存中的地址并不是固定的。由于当前是通过物理地址访问的。这样会导致 程序地址需要重定位。增加了开发的难度。并且也很容易产生物理内存碎片化。 最终导致可以用的物理空间越来越小。

综合, 动态分区法存在的弊端是:

  1. 进程地址空间无保护: 程序之间可以随意破坏。
  2. 程序运行地址需要重定位: 增加开发难度。
  3. 内存利用率低:碎片化严重。

大佬们为了解决上述三个问题, 提出了 分段机制和分页机制。

1.2 分段机制

对于 第一和第二个问题 进程地址空间没有保护 和 程序地址需要重定位。 人们使用虚拟内存的方式来解决。

进程 A 和 进程 B, 使用各自的虚拟内存,彼此之间无感。如果其中一个进程访问了没有映射的虚拟地址空间,或者访问了不属于该进程自己的虚拟空间,cpu 会捕获这个越界访问,并拒绝该访问。

由于程序运行在各自的虚拟内存中, 并不需要关心物理地址。它运行时只关心虚拟地址。工程师们也不用像在 动态分区中那样就关心 每个进程的物理运行地址。这样就避免了 程序地址的重定位。

请看如下场景:

在这里插入图片描述

场景一:

  • 进程 A B C 各自在自己的 虚拟地址中运行。 但是他们都被映射到不同的物理地址中。 如果此时要想运行 进程D, 由于物理地址没有 5MB 大小的空间。 此时就要 将进程 A 交换出去。 交换出去如下图。

在这里插入图片描述

场景二:

  • 此时 进程 C 和 进程 D 中间就有 1 MB的空洞。

虽然我们的程序可以分为 数据段、程序段、堆段、栈段。 但是在每次物理空间不够时, 依然 会以进程为单位将我们的进程给交换到磁盘中。

总结:

  • 分段机制中引入了 虚拟地址空间的概念, 很好的解决了 地址空间无保护和 地址重定位问题。
  • 但同时, 依然没有很好的解决 地址碎片问题, 而且按照程序为单位在内存和磁盘来交换,效率不高和资源浪费也同样是问题。

这种 按照进程为单位将整个进程资源交换到磁盘中的行为, 非常浪费系统资源。而且效率也不高。 并且每次进程在运行时,也并不需要 将整个程序加载到内存中。实际每次运行时只需要程序各自段中的一小部分。基于这种情况,人们就在思考, 能不能只将程序的一部分映射到物理内存中。 需要时在从磁盘中去加载对应的部分。 基于这种思路, 人们发明了 分页机制。

1.3 分页机制

由于在分段机制中, 地址映射的粒度比较大,以整个进程地址空间为单位来映射物理地址空间。 这样导致内存利用率不到。分页机制的出现很好的解决了这种问题。

分页机制中, 物理地址空间和虚拟地址空间都是按照页为单位来映射的。这样程序运行所需要的数据和代码还有堆栈都可以 按照页的大小来驻留到我们的内存中。而不常用的 内容也同样按照页的大小可以交换到磁盘中。从而节约了物理内存。

如图所示
在这里插入图片描述

  • 进程 A、B、C、D 各自运行在各自的虚拟内存空间中, 但是物理空间中只保留了 程序运行所需的物理页。其余都可以交换到物理磁盘中。这样很大的程度上提高内存的使用效率。

在这里插入图片描述

上面的图描述了 32 位虚拟地址 如何转化为 物理地址。

  • cpu 中 TTBR 中保存该进程的 一级页表的物理地址。 我们拿到一个虚拟地址后,的[31:20] 描述了当前一级页表中页表项的偏移量。 通过它就可以从一级页表中找到对应的页表项。从而找到二级页表的起始物理地址。
  • 虚拟地址的[19:12] 中代表了 二级页表中的偏移地址。 通过它, 结合上一步找到的二级页表的起始地址。就能找到 对应的二级页表项。在二级页表的页表项中保存了 [31:12] 物理地址。
  • [31:12] 物理地址 + 虚拟地址的[11:0] 就是最终的物理地址。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值