内存管理是任何操作系统的重点和难点。它旨在为系统中的所有Task提供稳定可靠的内存分配,释放与保护机制。
Android中的音频系统,GUI系统以及Binder的实现机理等,都与内存管理是息息相关的,内存管理的底层原理基本都是不变的。以下是几个核心的理解点:
- 虚拟内存
- 内存分配与回收
- 内存保护
1. 虚拟内存
随着软件体积的增大,出现了机器不能一次性将整个程序读入内存的情况。虚拟内存的出现为大体积程序运行提供了可能,它的基本思想是:
- 将外部存储器的部分空间作为内存的扩展(例如从硬盘划分4GB)
- 当内存资源不足的时候,系统将按照一定算法自动挑选优先级低的数据库,并把它们存入硬盘中
- 后续如果需要用到硬盘中的这些数据库,系统将产生“缺页”指令,将它们交换回内存中。
- 这些操作都是由操作系统内核自动完成的,对上层应用完全透明
1.1 逻辑地址(Logic Address)
它又被称为相对地址,是程序编译后产生的地址,由两部分组成:
Segment Selector:段选择子
用于描述逻辑地址所处的段,16bit
TI:Table Indicator,它的值对应的是两个table——
- GDT (Global Descriptor Table):有效范围是全局的
- LDT (Local Descriptor Table):进程自己创建本地表,以增加额外的段
他们都是用来记录各种段描述符(Segment Descriptors),而表本身的存储地址则是由GDTR和LDTR这两个cpu寄存器来保存
因此,TI的作用就是代表GDTR/LDTR中的序号:0表示GDT,1表示LDT。
**Offset:段内偏移值
用于描述段内的偏移值,32bit。
CPU提供了专用的寄存器来承载段选择子
寄存器名称 | 描述 |
---|---|
CS | Code Segment Register,代码段寄存器 |
DS | Data Segment Register,全局与静态数据段寄存器 |
SS | Stack Segment Register,堆栈段寄存器 |
ES | Extra Segment Register,附加数据段寄存器 |
FS,GS | 通用段寄存器 |
逻辑地址需要两次变换才能得到物理地址:
并不是所有的操作系统都同时致辞后段页式的内存管理,有些操作系统只提供了页式的管理机制,而Linux 内核虽然理论上式段页式的,但也只是实现了分页机制(分段机制只用到了一部分功能)。
1.2 线性地址(Linear Address)
线性地址式根据逻辑地址经过分段机制后形成的,其基本思想:
- 根据段选择子中的TI字段,得知存储在GDT/LDT中
- 通过GDTR/LDTR获得GDT/LDT的存储地址。
- 根据段选择子中的INDEX字段,到GDT/LDT中查找对应的段描述符。
- 根据段描述符获得此段的基地址。
-
由基地址+段内偏移值得到线性地址
1.3 物理地址
开启了分页机制后,线性地址还需要一次转换才能到物理地址:
1.4 缺页
如图,当线性地址中某些与物理内存的页没有映射关系,就说明它们实际上暂时并不在内存中,就会发生缺页中断,操作系统此时会介入,通常式用算法将最不常用的页调出内存,从而为线性地址中的“缺失页”腾出位置,然后将“缺失页”从外存储器重新取回,然后返回中断点,继续操作。