第八章-内存管理系统

Ⅰ.位图

本质是通过建立一张表管理资源,表中每个位置对应的位,与每个资源一一对应。bit-map,即位置与资源的映射关系。

位图结构体:

struct bitmap{
    uint16_t btmp_bytes_len;            // 位图长度
    uint8_t *bits;                      // 位图数组指针,长度为字节,8位对应8个资源
}

Ⅱ.内存管理系统

用户程序所占用的内存空间是由操作系统分配的,内存是如何分配的并且该给用户进程分配多少字节呢?

在保护模式下,程序地址变成了虚拟地址,虚拟地址对应的物理地址是由分页机制做的映射 。因此,在分页机制下有了虚拟、物理这两种地址,操作系统有责任把这两种地址分别管理,并通过页表将这两类地址关联。

物理内存池和虚拟内存池

由于在分页机制下有了虚拟地址和物理地址,为了有效地管理它们,我们需要创建虚拟内存地址池和物理内存地址池。

第八章-内存管理系统_第1张图片

内存池的创建包括两部分:基础空间(在执行程序前必须分配的内存空间,如进程、线程的PCB、内核文件),和可用空间。

PCB所占用的内存必须是自然页,大小为4KB,一个自然页以0xXXXXX000为开始地址,根据单位大小可知,结束地址为0xXXXXXfff。PCB的数据结构为以0xXXXXX000地址往上存进程、线程信息(如pid,进程状态等),0xXXXXXfff地址往下的空间属于0特权级下的进程或线程的栈。

(1)物理内存池

把物理内存分成两个内存池,一部分称为用户物理内存池,此内存池中的物理内存只用来分配给用户进程。另 一部分就是内核物理内存池,此内存池中的物理内存只给操作系统使用。

第八章-内存管理系统_第2张图片

当用户内存池中的内存都被用户进程耗尽时,不再向内核内存池申请,而是返回信息“内存不足“,拒绝请求。

(2)虚拟内存池

对用户进程来说,它向内存管理系统,即操作系统,申请内存时,操作系统先从用户进程自己的虚拟地址池中分配空闲虚拟地址,然后再从用户物理内存池(所有用户进程共享〉中分配空闲的物理内存,然后在该用户进程自己的页表将这两种地址建立好映射关系。

虚拟地址的范围取决于地址总线的宽度,在 32 位环境下,虚拟地址空间为 4GB。

具体实现过程:

高1G的空间分配,低1MB空间与物理地址一一映射,对应系统内核文件。

(1)内核内存空间确定

由于内核文件需要执行,存在main入口函数,因此需要建立PCB执行程序,指定PCB大小为4KB。起始地址为0xc0009e000,结束地址为0xc0009f000,结束地址对应main程序的进程和线程的栈指针指向位置。

(2)确定位图的虚拟内存地址

位图是在PCB设置之前,位图大小为4页,一页为0x1000,那么位图起始地址为0xc0009e000-0x4000=0xc0009a000。

(3)确定内核程序的PCB空间

PCB占用内存大小为一个自然页,因此大小为4KB,地址紧接着位图之后,故地址范围为0xc0009e000-0xc0009f000。

(4)分配内存池

分配内存池离不开虚拟地址和物理地址的转换过程。

第八章-内存管理系统_第3张图片

32位虚拟地址被分为三部分,高10位对应页目录表的索引,中10位对应页表索引,低12位对应物理地址的偏移地址。

内存池分配过程
  1. 传入用户内存池/内核内存池pool_flag,以及申请内存大小pg_cnt
  2. 在位图中申请pool_cnt大的内存空间
  3. 成功,则将位图接下来的pool_cnt个位置1
  4. 此时已经完成了内存分配的任务,但对于已分配的内存,还需要将页目录项、页表项存入页目录表和页表
  5. loader.S中设置页目录表地址为页目录项的最后一项,即第 1023 个 pde ,对应十六进制0x3ff,将其移到高10位后为0xffc00000,
/* 得到虚拟地址vaddr对应的pte指针,最终的32位虚拟地址	*/
uint32_t* pte_ptr(uint32_t vaddr) {
   /* 页目录表地址 + \
    * 再用页目录项pde(页目录内页表的索引)做为pte的索引访问到页表 + \
    * 再用pte的索引做为页内偏移*/
   uint32_t* pte = (uint32_t*)(0xffc00000 + \
	 ((vaddr & 0xffc00000) >> 10) + \
	 PTE_IDX(vaddr) * 4);
   return pte;
}

/* 得到虚拟地址vaddr对应的pde的指针 */
uint32_t* pde_ptr(uint32_t vaddr) {
   /* 当 32 位地址中高 20 位为 0xfffff 时,这就表示访问到的是最后一个页目录项,即获得了页
目录表物理地址,因此0xfffff是用来访问到页表本身所在的地址 */
   uint32_t* pde = (uint32_t*)((0xfffff000) + PDE_IDX(vaddr) * 4);
   return pde;
}

总结:

为什么要有虚拟内存地址?
  • 程序之间彼此独立
  • 保护内核系统
  • 编译器在编译阶段可以直接按照用户需要编译
谁在维护程序页表?

页目录表、页表、物理页表共同完成了虚拟地址->物理地址,因此程序的执行会涉及到页表的访问,因此操作系统和用户程序共同维护页表

GDT、LDT表

在编译时就自动创建了。GDT中存储的是全局程序,即在整个系统周期内支持随时被调用的程序。其他的程序拥有自己的LDT,LDT中存储的是程序物理内存地址,根据分页置换的原则执行程序。当内存空间不足以运行程序时,将周期内A=1的次数最低的段移除到外存。当访问段的A位=0时,即内存中不存在段时,CPU抛出NP异常,中断处理程序将自动加载程序到内存中。

你可能感兴趣的:(操作系统真象还原,操作系统)