第 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 # 加一层抽象加强内存管理
为了解决这种困境,抽象仍然是最重要的指导思想。在这里,抽象意味着内核要负责将物理内存管理起来,并为上面的应用提供一层抽象接口,从之前的失败经验学习,这层抽象需要达成下面的设计目标:
- 透明 :应用开发者可以不必了解底层真实物理内存的硬件细节,且在非必要时也不必关心内核的实现策略, 最小化他们的心智负担;
- 高效 :这层抽象至少在大多数情况下不应带来过大的额外开销;
- 安全 :这层抽象应该有效检测并阻止应用读写其他应用或内核的代码、数据等一系列恶意行为。
“透明”这个说法不知来源,可能是想表达“不被感知”但是失败了,容易让人误会。含义其实与透明的直觉感受相反————“屏蔽”。下层屏蔽细节,把封装好的内容提供给上层。
作业 ¶
没有,开心。