在讲解段式内存管理、页式内存管理之前,需要了解X86体系结构中的实模式和保护模式相关内容。
在 X86 架构诞生之初,其实是没有虚拟内存的概念的。1978 年发行的 8086 芯片是 X86 架构的首款芯片,它在内存管理上使用的是直接访问物理内存的方式,这种工作方式,有一个专门的名称,那就是实模式(Real Mode)。直接访问物理内存的工作方式让程序员必须要关心自己使用的内存会不会与其他进程产生冲突,为程序员带来极大的心智负担。
后来,CPU 上就出现虚拟内存的概念,它可以将每个进程的地址空间都隔离开,极大地减轻了程序员的负担,同时由于页表项中有多种权限保护标志,极大地提高了应用程序的数据安全。所以人们把 CPU 的这种工作模式称为保护模式(Protection Mode)。
从实模式演进到保护模式,X86 体系架构的内存管理发生了重大的变化,最大的不同就体现在段式管理和中断的管理上。我们会围绕这两个重点,让你彻底理解X86 体系架构下的内存管理演进。学会阅读 Linux 内核源码的段管理和中断管理的相关部分,还可以增加调试 coredump 文件的能力。
这里我们就按照时间顺序,从 8086 芯片中的实模式开始讲起。
8086 芯片是 Intel 公司在 1978 年推出的 CPU 芯片,它定义的指令集对计算机的发展历程影响十分巨大,之后的 286、386、486、奔腾处理器等等都是在 8086 的基础上演变而来。这一套指令集也被称为 X86 指令集。
8086 的寄存器只有 16 位,我们也习惯于称 8086 的工作模式是 16 位模式。而且,后面的 CPU 为了保持兼容,在芯片上电了以后,还必须运行在 16 位模式之下,这种模式有个正式的名字,叫做实模式(Real Mode)。在实模式下,程序员是不能通过内存管理单元(Memory Management Unit, MMU)访问地址的,程序必须直接访问物理内存。
在操作系统中,页式内存管理是非常重要的内容,页式内存管理,引出了多任务程序设计的基础。
我们知道计算机上电之后,会实模式跳转到保护模式的。那么这个保护模式,是保护什么的?
这里的保护模式是保护内存段的,在X86处理器中内置的保护模式,一旦保护模式打开,那么某一段内存的访问就是受限的,这里的受限就是指受保护的。保护模式就是来保护一段连续的内存空间,这里的一段连续的内存空间,我们简称为段。
一段连续的内存空间。
应用程序规模越来越大,导致多数时候无法全部加载进内存,如何解决?
有更进一步的解决方案是,在段的基础上,进行分页。
应用程序数据段、代码段、堆栈段 是相对独立的。
每个段都是有各个页组成的
代码段在运行过程中,会去加载数据段的内容,也会去加载堆栈段的内容。
大多数情况下,书都采用“段页式”的方法进行内容编排。
书是由一章一章组成的,一章一章不固定,然后一章一章是由页组成的。
章的大小不固定,就相当于程序中的段,页的大小固定,相当于程序中的页。
虚拟内存空间(逻辑地址):程序执行时内部所使用的内存空间(独立于其他程序)
物理内存空间(物理地址):物理机器所配置的实际内存空间(所有程序共享)
虚拟地址(逻辑地址)需要进行转换(内存映射)才能得到对应的物理地址。
逻辑地址(虚拟地址)到物理地址的映射(重定位)
0XAABBCC12(逻辑地址) --> MMU --> 0xDDCC12(物理地址)
MMU会去查询地址映射表(页表)
逻辑页号 | 物理页号 | 属性 |
..... | ...... | ...... |
0XAABB | 0XDD | 0X00 |
...... | ...... | ..... |
0XAABBCC12(虚拟地址) 到 MMU中去查表,发现逻辑页号 0XAABB 存在页表中,然后对应的物理页号是0XDD,所以把虚拟地址的高位AABB 替换成 DD,低位不变。所以最后的物理地址就是:0XDDCC12
访问一个逻辑地址(虚拟地址)时,对应的页不在内存中
页请求时发现物理内存不足,需要将暂时不用的页移除
#include
#include
#include
#include
#include
using namespace std;
#define PAGE_NUM (0xFF +1) //定义一个任务最多有256页
#define FRAME_NUM (0x04) //物理内存只有4个页
#define FP_NONE (-1)
struct FrameItem
{
int pid; // the task which use the frame
int pnum; // the page which the frame hold
FrameItem()
{
pid = FP_NONE;
pnum = FP_NONE;
}
};
class PageTable
{
int m_pt[PAGE_NUM]; //表示每一个任务最大的虚拟内存页数,从第0页 到第 255页。
public:
PageTable()
{
for(int i=0;i
//未完待续.....