1.当一个程序需要使用一块不存在的内存时(也即在内存页表项中已标出相应内存页面不在内存中), CPU 就需要一种方法来得知这个情况。这是通过 80386 的页错误异常中断来实现的。
2.当一个进程引用一个不存在页面中的内存地址时,就会触发 CPU 产生页出错异常中断,并把引起中断的线性地址放到 CR2 控制寄存器中。因此处理该中断的过程就可以知道发生页异常的确切地址,从而可以把进程要求的页面从二级存储空间(比如硬盘上)加载到物理内存中。
3.如果此时物理内存已经被全部占用,那么可以借助二级存储空间的一部分作为交换缓冲区( Swapper )把内存中暂时不使用的页面交换到二级缓冲区中,然后把要求的页面调入内存中。
4.这也就是内存管理的缺页加载机制,在 Linux 0.11 内核中是在程序 mm/memory.c 中实现
5.Intel CPU 使用段( Segment )的概念来对程序进行寻址。每个段定义了内存中的某个区域以及访问的优先级等信息。而每个程序都可有若干个内存段组成。程序的逻辑地址(或称为虚拟地址)即是用于寻址这些段和段中具体地址位置。
6.在 Linux 0.11 中,程序逻辑地址到线性地址的变换过程使用了 CPU 的全局段描述符表 GDT 和局部段描述符表 LDT 。由 LDT 映射的地址空间称为全局地址空间,由 LDT 映
射的地址空间则称为局部地址空间,而这两者构成了虚拟地址的空间。具体的使用方式见图 2-10 所示。
7.图中画出了具有两个任务时的情况。对于中断描述符表 idt ,它是保存在内核代码段中的。由于在Linux 0.11 内核中,内核和各任务的代码段和数据段都分别被映射到线性地址空间中相同基址处,且段限长也一样,因此内核和任务的代码段和数据段都分别是重叠的。另外, Linux 0.11 内核中没有使用系统段描述符。
8.内存分页管理的基本原理是将整个主内存区域划分成 4096 字节为一页的内存页面。程序申请使用内存时,就以内存页为单位进行分配。
9.在使用这种内存分页管理方法时,每个执行中的进程(任务)可以使用比实际内存容量大得多的连续地址空间。
10.对于 Intel 80386 系统,其 CPU 可以提供多达 4G 的线性地址空间。对于 Linux 0.11 内核,系统设置全局描述符表 GDT 中的段描述符项数最大为 256 ,其中 2 项空闲、 2 项系统使用,每个进程使用两项。因此,此时系统可以最多容纳 (256-4)/2 + 1=127 个任务,并且虚拟地址范围是 ((256-4)/2)* 64MB约等于 8G 。
11.但linux 0.11 内核中人工定义最大任务数 NR_TASKS = 64 个,每个进程虚拟地址范围是 64M ,并且各个进程的虚拟地址起始位置是 ( 任务号 -1)*64MB 。因此所使用的虚拟地址空间范围是 64MB*64 =4G ,4G 正好与 CPU 的线性地址空间范围或物理地址空间范围相同,因此在 0.11 内核中比较容易混淆三种地址概念。
12.进程的虚拟地址需要首先通过其局部段描述符变换为 CPU 整个线性地址空间中的地址,然后再使用页目录表 PDT (一级页表)和页表 PT (二级页表)映射到实际物理地址页上。
13.为了使用实际物理内存,每个进程的线性地址通过二级内存页表动态地映射到主内存区域的不同内存页上。因此每个进程最大可用的虚拟内存空间是 64MB 。每个进程的逻辑地址通过加上任务号 *64M ,即可转换为线性地址。不过在注释中,我们通常将进程中的地址简单地称为线性地址。
14.从 Linux 内核 0.99 版以后,对内存空间的使用方式发生了变化。每个进程可以单独享用整个 4G 的地址空间范围。