1. 前言
《Linux内核完全注释》这本书提到了几种Linux内核中的几种地址,实地址,有虚拟地址,逻辑地址,线性地址,物理地址。除了物理地址以外,其他几种容易弄混淆。这里做一下笔记,讲一下我的理解(以32位系统为例)。
2. 几种地址的理解
这几种地址,我觉得不要从字面上去理解,有的技术词汇,从字面上理解是很难的,它可能仅仅就是为了和其他的概念做区分。我觉得要结合用法和使用场景上去理解。
我们知道CPU对于内存的管理分为两种模式:实模式和保护模式。
在实模式下:
由CS和IP寄存器构成实地址,这个地址由CPU执行的时候,就是直接把CS和IP构成一个地址后,放到地址线上,选中某个内存地址,此时用数据总线去读数据。整个过程简洁明了,没有弯弯绕。
由上,实地址就是实模式下代码使用的地址。CPU最终放到数据地址线上的地址,是物理地址。
在保护模式下:
保护模式下,又有两种内存管理机制:
- 内存分段
- 内存分页
这两种机制本质上的作用就是地址变换,也就是应用程序开发中的中间层结构。CPU中,分段总是位于分页之前进行,保护模式下分段总是开启,分页可以不开启。
下面是两个机制各自进行的地址转换过程:
- 内存分段机制,将虚拟地址转换为线性地址。虚拟地址和逻辑地址等同。
- 如果启用了内存分页机制,则内存分页机制将分段机制输出的线性地址转换为物理地址。如果没有启用内存分页机制,则线性地址就直接等于物理地址,被直接放到CPU的地址线上,用做物理地址。
如果只是讲到这,好像是讲解了,其实就像是什么都没说。
3. 分段机制
CPU分段机制,涉及到GDT,LDT这两种内存中的表,分别由当前的GDTR,LDTR寄存器指定它们在内存中的位置。
两种地址映射表,每个表其实就是一个将内存分成很多段,段可以重叠,分段的起始地址和长度,以及分段的权限等信息,都保存在表项中。
GDT是全局的,整个系统只有一张,LDT是每个进程都有一张。每个表都可以对每个表代表的4G的内存空间进行各自的规划。规划好了以后,怎么去访问物理内存呢?每个进程都有一张LDT,代表了每个进程都有4G的内存空间。那么问题来了,有那么多物理内存吗?肯定是没有,但是我们可以模拟呀。怎么模拟?利用分页机制来模拟。
4. 分页机制
CPU分页机制,涉及页目录表,页表。页是指固定大小的物理内存。我们把一整个物理内存分成很多页来管理,索引信息放到页目录表和页表中,便于查找和动态管理。
分页机制简单来说就是把GDT,LDT中计算出来的地址,再次进行转换,通过页目录表和页表查找有没有物理内存页可用,如果没有找到,就腾出一个物理内存页,把索引放到页目录表和页表中,进行读写使用。
物理内存页不够用怎么办?怎么腾出物理内存页?内核把不常用的物理内存页中的信息保存到硬盘上,这个物理内存页就腾出来了。
如果保存到硬盘上的信息需要被某个程序用到,那么就再腾出来一个物理内存页,把信息从硬盘上再读取出来使用。
说白了,就是临时借用,谁着急谁先用,分时复用,腾出来的信息放到硬盘上,硬盘容量大,肯定存的下。当然读写硬盘比较慢,可以认为这是时间换空间的做法。
5. 结语
本文只是辅助梳理思路,抛砖引玉,如果想要掌握内存管理机制,绝对不能停留在这个水平,一定要看具体实现细节,这里推荐《Linux内核完全注释》,比很多资料强上不少。