内存管理方案
分区:
程序加载时,将程序整体加裁到一块分区上
- 固定分区 内存碎片
操作系统启动时,将内存分配完好。会产生内部碎片
1.1 大小相等的固定分区
初始化的时候将内存分配为大小都相等的区块
1.2 大小不等的固定分区
初始化的时候将内存分配成大小不等的区块
1) 为每一个区块维护一个等待队列
2) 整体维护一个队列 - 动态分区 外部碎片
在程序进程运行时,才会动态的分配内存空间。会产生外部碎片
分页:
类似于大小相等的固定分区上做拓展,系统初始化时,将内存划分为大小相等的页帧 (4K),程序在磁盘上的存储,也是按照 4K 的页面进行存储。加载程序时,操作系统为每一个进程维护一个页表。一个进程可以不连续的在内存上占据多个页帧。
分段:
在动态分区上做拓展。加载程序时,根据程序的段,将程序加载到内存上,操作系统为每一个进程维护一个段表。一个进程可以不连续的在内存上占据多个段。每个页帧的大小是固定的,而每个段是根据程序的段分大小。大小不固定
伙伴系统:
分配时,如果可用的空间很大。则将其一分为二,直到分配的空间不能再分(如果再次分割,则小于申请空间),就将其中一个分配给申请的进程。回收空间时,如果左或者右有和他相等的空闲区块,则合并为一个大的区块(不是一次合井) 。用二叉树进行映射,每个叶子节点对应一块内存区域
交换分区:
(在磁盘上扩展的内存空间)在磁盘上开辟一块空间以管理内存的方式管理这块磁盘空间,作为对内存的补充。
当内存空间不足时,才将内存上的部分数据交换 ( MMU ) 到交换分区上。
1、可以在内存上驻留更多的进程
2、可以执行比内存大的进程
虚拟内存:
1、把最久未使用的页面放入磁盘交换分区,把活动的页面放入物理内存中。
2、使操作系统给每一个进程分配一个4G的虚拟地址空间
3、保证每个进程的空间都是独立的。
缺页异常:
由于代码在不合法的情况的下造成的代码异常。发生以后执行异常处理程序
缺页中断:
中断是写在代码里面,中断是执行中断处理程序,执行完以后从中断的下一行开始执行
8086系统
8086 在 CPU 增加了 4 个段寄存器 CS(代码段寄存器) DS(数据段寄存器) SS(堆栈段寄存器)(ES(扩展段寄存器)) IP 寄存器存放偏移量。这些寄存器都是 2 个字节,占 16 位。
8086 共有 16 位,但是地址总线有 20 位。CS 存储的不是真正的内存起始地址,而是内存的高 16 位。在 8086 中,但是物理内存中每个内存段的起始地址是 16 的倍数,能被16整除必然是因为二进制的后四位都是 0,所以 20 位的低 4 位全是 0,所以可以把高 16 位存储到 CS 中。
80386 之后添加的两个寄存器
GDTR:全局的段描述附表。里面存 32 位 GDT 的地址
LDTR:局部的段描述附表
GDT:全局的段描述附表,里面存放一个数组,数组里面存放内存的起始地址、内存段的大小、内存的访问权限,数组的下标是段寄存器。一个段描述附表占 8 个字节
内存段的起始地址
描述符中的前 12 位被内核占用,能给用户进程的有 8180 个
红色区域是内存的起始地址,共 32 位,蓝色区域是长度,共 20 位
220 = 1M G 取 0 表示段描述符表项描述的是字节(byte),段描述符能描述的最长字节是1M*1=1M。
G 取 1 表示段描述符表项描述的是页面(4k),段描述符能描述的最长字节是 1M * 4k = 4G 。32 位 Linux 内核给每一个进程的运行都会分配一个虚拟地址空间 232 = 4G,3G 用户空间 1G 内核空间
实模式
实地址模式—》实模式
实模式是 16 位模式,有 20 位地址总线,共占 1M 的空间,里面存储了开启操作系统等的程序,操作系统运行时,从 0x100000(1M) 开始运行。
在实模式下进行数据的访问是不安全的。因为是直接访问,没有经过操作系统
地址映射
DS << 4 + IP = 数据的地址(物理地址)
段基址 偏移量/偏移地址/逻辑地址(一个内存上的偏移量)
保护模式
内存的分段式映射
GDT [DS>>3]. BaseAddr + (IPE < Length) = 线性地址
段基地址 逻辑地址
检查有没有开启分页机制
CR0 最高 PG 位 0 表示没有开启内存分页,1 表示开启内存分页
CR2 发生缺页异常的虚拟地址
CR3 页目录的起始地址
CR4 PAE 位 物理地址扩展 0 表示未开启,1 表示开启
内存的分页式映射
0x0018ff44 是线性地址(虚拟地址),共 32 位,把 32 位共分成 3 部分,分别为 10、 10、12
xxxxxxxxxx xxxxxxxxxx xxxxxxxxxxxx
页目录的下标 页表的下标 物理内存上的偏移量
PG(页目录)是一个共有 1024 个元素的数组,数组的每一个元素是 4 个字节的指针,一个 PG 的大小是 1024 * 4 = 4k,CR3 存放当前页目录的起始地址。
PT(页表)是一个共有 1024 个元素的数组,数组的每一个元素是 4 个字节的指针,一个 PT 的大小是 1024 * 4 = 4k
物理页面共有 212 = 4k 的空间
页目录、页表和物理页面都是 4k,页目录和页表都在物理内存存储,存储方式按物理内存存储。物理内存从 0 开始,因此,页目录、页表和物理页面的起始地址都是 4k 的整数倍,他们的低 12 位是 0。在页目录里面,每个元素共 32 位,只用高 20 位存储 PT 的地址,用低 12 位存权限。
在页表里面,每个元素共 32 位,只用高 20 位存储物理页面的地址,用低 12 位存权限。低 12 位中的最低位是 present 位。
若 value = 0,present = 0,物理页面还没有分配过
若 value != 0,present = 0,PTE 映射的物理页面在交换分区中 value;物理页面在磁盘 swap 分区的位置
若 value != 0,present != 0,PTE 对应的物理页面是一个活动的页面
物理页面又称为物理页面的框号。低 12 位全是 0,地址分别是 0x00000、0x00001、0x00002、0x00003……,物理页面的地址在页表中以下标的方式存储。
虚拟地址的最高的 10 位计算成十进制的数字,以该数字为页目录PG的下标。每个页目录下存放下一级的页表的地址(PT),从 PT 得到物理页面的起始地址再加上物理页面的偏移量得到物理页面的起始地址
内核如何管理物理内存
内核通过数组管理物理内存,数组的每一项的是物理内存的信息。在内核初始化的时候检测物理内存有多大,若物理内存是 4G 大小,会根据一个页面是 4k 大小,划分出 220 个页面。220 将作为一个数字用指针动态开辟一个数组,数组的每一个元素都是 struct page 类型。访问数组的下标存储在PT里面。
当前进程有 1024 个 PG 项,一个 PG 项指向 1024 个 PT,因此,整个二级页目录页表映射能映射 4G 的物理内存,正好能映射完 4G 的虚拟地址空间。
能不能在内核空间定义一个指针指向用户空间的内存
可以。访问页面不用关心页面是否在物理内存中。如果页面在交换分区中,会触发 MMU 缺页异常,缺页异常交给内核,内核触发页面交换机制,牺牲一个脏页,把页面交换到物理内存中
磁盘中的可执行程序中有两个 Load 项,分别存放代码段和数据段,是按照页面进行对齐,可执行程序先映射到虚拟地址空间上,虚拟地址空间也是按照页面进行操作。虚拟地址必须通过页面映射到物理内存,物理内存也是按页面划分管理。
Load1 text 0x100
Load2 data 0x100