内存管理的历史
早期,程序员自己管理主存,通过分解程序并覆盖主存的方式执行程序
- 取指令和存储操作数所有的地址都是物理地址;
- 执行速度快,无需进行地址转换;
- 未采用虚拟存储机制。
1961年有人提出自动执行overlay的方式,使得程序员编程时不用管主存容量的大小。
基本思想
- 把地址空间和主存容量的概念区分开来,程序员在地址空间里编写程序,程序则在真正的内存中运行;
- 由一个专门的机制实现地址空间和实际主存之间的映射;
- CPU 中的 MMU (Memory Management Unit) 负责将逻辑地址(即虚拟地址)转换为内存的物理地址。
逻辑地址(即虚拟地址)转换为内存的物理地址的方式分3种:
分页式分段式段页式:程序按模块分段,段再分成长度固定的页,程序调入调出按页面来进行,程序共享保护按段进行,兼备段式,页式管理的优点,在地址映像中需要多次查表
段
根据程序的模块化性质,按程序的逻辑结构划分成多个相对独立的部分,称为段。段通常有段名、段起点、段长、段属性等信息;一个程序可以有多个代码段,多个数据段。
C语言程序中,变量的定义和指令写在一起,无分段的概念
但在机器语言层次上是要分段的,在C程序编译时,将变量的空间分配和指令分开,分别放在不同段中。
主存分段管理
在分段内存模型中,每个分段通常加载不同的分段选择器,以便每个段寄存器指向线性地址空间内的不同段。
主存物理地址的形成
实方式
物理地址 = (段寄存器)左移4位 +偏移地址
- 32位CPU与8086一样,只能寻址1M(2^20)物理存储空间
- 可以访问6个段:CS, DS, SS, ES, FS, GS,每个段至多64K(2^16)
- 段开始单元的物理地址 / 16 ➡ 段址
段中某一存储单元的地址用两部分表示——“段首地址:偏移地址”,称二维的逻辑地址。(注意区别段首地址和段址,段首地址 / 16 = 段址)
8086中,只有4个段寄存器 CS, DS, ES, SS
- 在代码段中取指令时:指令物理地址 PA = (CS) 左移四位 + (IP) ,使用的是IP而不是EIP
- 在数据段中读/写数据时:数据的物理地址 PA = (DS或ES)左移四位 + 16位偏移地址 (偏移地址由寻址方式确定)
- 在堆栈操作时:栈顶的物理地址 PA = (SS) 左移四位 + (SP)
保护方式
在多任务环境下,系统中有多个程序在运行,程序之间要隔离。
分段是存储管理的一种方式,为保护提供基础,
不同程序在不同段中,一个程序可以包含多个段,段用于封闭具有共同属性的存储区域;
描述符
存放为保护一个段需要的信息
- 段的起始位置(段基地址)
- 段的大小(段界限)
- 段的特权级
- 段的属性(是代码段、数据段还是堆栈段?数据段是否可写?代码段是否可读出?)
- 段的位置(在内存还是在磁盘?)
- 段的类型(在系统段还是用户段?)
- 段的使用(段被访问过,还是没有?)
描述符表
描述符的集合。
局部描述符表(LDT):是一个系统段,最大可为64KB,最多可存放8192个描述符(一个描述符8B)。
对每一个程序,都建立一个局部描述符表(LDT)。
全局描述符表(GDT):只有一个。GDT最大可为64KB,存放8192个描述符。
包括:
- 操作系统所使用的段的描述符;
- 各个LDT段的描述
保护方式下物理地址的形成
STEP1——从虚拟地址到线性地址
xxxx指出找相应段描述符的方式,称段选择符。
1. 若TI 位为 0 (操作系统段)
• 从GDTR寄存器中获取GDT的基址;• 在GDT表中,以 XXXX 的高13位作为索引,取出一个描述符A;• 描述符A中的段基地址 + yyyyyyyy : 为要访问单元的线性地址。
2. 若TI 位为 1 (用户程序段)
• 从GDTR寄存器中获取GDT的基址;• 在GDT表中, 以 LDTR的高13位 作为索引,取出一个描述符A;• 描述符A描述的段为一个LDT段(LDT_A) ;• 用 XXXX的高13位,作为索引,在LDT_A段中找到描述符P_A。• P_A描述段的基址+yyyyyyyy 为线性地址。