跳转至

13 章 抽象:地址空间

终于,在年后追上了 Rust 内核项目的进度……比预期的晚了一些(看时间线就知道忙了些什么七七八八的)

参考:rCore-Tutorial-Book-v3 # 地址空间

13.1 早期系统

早期的操作系统类似于函数库,在内存中从物理地址 0KB 开始到 64KB 结束,而应用程序则从物理地址 64KB 开始,继续使用剩余的内存。

Memory Data
0KB - 64KB OS (code, data, etc.)
64KB - MAX Program (code, data, etc.)

物理内存的一部分用来保存操作系统(函数库)的代码和数据,余下的部分都交给应用来使用。

从功能上应用的内存数据可以分为几个段:

  • 代码段
  • 全局数据段
  • ...

由于只有一个应用,如何调整布局是它自己的事情。

这个时期,内核提供给应用的访存视角一致,它们始终独占一块固定的内存区域,每个应用的开发者都基于这一认知来规划程序的内存布局。

(批处理系统也一样,只不过它会在下一个应用运行前清空上个应用占据的内存区域)

13.2 多道程序和时分共享

为了降低等待 I/O 操作带来的资源消耗,多道程序出现了。

为了提升用户的交互式体验,分时多任务系统诞生了。

这个时代的操作系统中会同时有多个程序在运行,当程序在等待 I/O 操作或执行足够长时间后时会被“暂停”,操作系统负责切换到另一个程序。

当它们处于暂停状态时,驻留在内存中的代码、数据该何去何从呢?

一种方式是将这些代码、数据存放到磁盘上,然后把即将换入的应用在磁盘上的代码、数据恢复到内存。

这种做法需要大量读写外部存储设备,而它们的速度都比 CPU 慢上几个数量级,这导致任务切换的开销过大,甚至完全不能接受。

因此,可以在进程切换的时候,仍将进程信息留在内存中,也就是内存中同时存放所有进程的内存,代价是需要限制每个应用的可用内存大小。

Memory Data
0KB - 64KB OS (Code, Data)
64KB - 128KB (free)
128KB - 192KB Process C (code, data, etc.)
192KB - 256KB Process B (code, data, etc.)
256KB - 320KB (free)
320KB - 384KB Process A (code, data, etc.)
384KB - 448KB (free)
448KB - 512KB (free)

从应用开发的角度来看,这种做法给开发带来了一定困难,因为需要自己决定加载到哪个物理地址运行,要求开发者对硬件特性和使用方法有更多了解,额外的学习成本。

从内核的角度来看,应用能够直接访问物理内存,就也可以读取或修改其他进程(甚至操作系统)的内存,这是极大的安全隐患。

13.3 地址空间

Memory Data Description
0KB - 1KB Program Code 代码段:指令所在位置
1KB - 2KB Heap 堆段:包括 malloc 分配的数据,向下增长
2KB - 15KB (free)
15KB - 16KB Stack 栈段:包含局部变量、函数的参数、返回值等,向上增长

以上是操作系统提供给运行程序的抽象,程序实际不在物理地址 0 ~ 16KB 的内存中,而是加载在任意的物理地址,程序会认为自己被加载到特定地址(例如 0)的内存中,并且具有非常大的地址空间,这都是虚拟化内存 (virtualizing memory) 的功劳,而程序访存使用的地址称为虚拟地址 (virtual address)

从此,操作系统拥有了 特权级 + 地址空间 两重安全措施。

13.4 目标

rCore-Tutorial-Book-v3 # 加一层抽象加强内存管理

为了解决这种困境,抽象仍然是最重要的指导思想。在这里,抽象意味着内核要负责将物理内存管理起来,并为上面的应用提供一层抽象接口,从之前的失败经验学习,这层抽象需要达成下面的设计目标:

  • 透明 :应用开发者可以不必了解底层真实物理内存的硬件细节,且在非必要时也不必关心内核的实现策略, 最小化他们的心智负担;
  • 高效 :这层抽象至少在大多数情况下不应带来过大的额外开销;
  • 安全 :这层抽象应该有效检测并阻止应用读写其他应用或内核的代码、数据等一系列恶意行为。

“透明”这个说法不知来源,可能是想表达“不被感知”但是失败了,容易让人误会。含义其实与透明的直觉感受相反————“屏蔽”。下层屏蔽细节,把封装好的内容提供给上层。

作业

没有,开心。