Skip to content

Latest commit

 

History

History
164 lines (131 loc) · 9.71 KB

PA4.1 Talk.md

File metadata and controls

164 lines (131 loc) · 9.71 KB

PA4.1 授课内容

作者:南京航空航天大学 金航

未经作者允许,禁止转载

回顾目前的NEMU/AM/Nanos体系

现在能做什么?

  • 能够加载用户程序
  • 能够响应处理中断
  • 能够进行文件读写
  • 能够进行设备访问
  • 能够跑仙剑奇侠传

现在不能做什么?

  • 运行效率还不够高:受限于模拟器本身
  • 不能同时运行多道程序:实现分时多任务

分时多任务

分时多任务的条件

  • 独立存储空间(即位置无关的代码和数据)
  • 进程分时切换(即各个进程轮流使用处理机)

独立的存储空间

方案1:位置无关代码PIC

即通过全局偏移量表实现可以将程序加载到内存任意位置上都可以正常运行,可惜Nanos-lite无法实现,因为我们实现的loader只能直接复制用户程序到指定位置,不具备读偏移量表的功能,很遗憾。

方案2:虚拟地址空间

  • 我们仍选择始终将用户程序加载到同一个地址上,有人会说:我在运行仙剑,但是后又运行hello,那么hello的代码就把仙剑的代码冲掉了啊?
  • 我们将该加载程序的地址称为虚拟地址空间,意味着程序认为他自己在这里,但是实际上在物理内存中,并不在这里,我们只需要维护好虚拟地址空间到真实物理地址之间的映射关系就可以了
  • 这个过程是一个软硬件结合管理的机制,因为程序看到的虚拟地址往哪里映射由OS维护,负责管理内存,决定映射的地址。而程序运行时的地址空间转换就需要由硬件维护,因为OS无权干涉指令运行。

分段

我们并不实现分段,但是了解一下分段,即段寄存器中存段选择子,然后从GDT表中找到段描述符,然后和偏移相加得线性地址

分页

  • 按需分配,以免造成地址空间浪费
  • 粒度比分段小得多,便于组织管理
  • 分页机制的地址转换图见讲义

页级地址保护

  • 注意对页表的P位是否可用位进行检验
  • R/W位进行写权限检验
  • U/S位进行访问权限建议(即拦截ring 3试图访问ring 0

新的硬件:MMU

  • 内存管理单元,属于硬件,用于虚拟地址到物理地址的转换
  • 操作系统在加载程序时将程序相关的页表信息告诉MMU
  • MMU根据页表内容和映射关系进行地址转换
  • MMNanos-litemm.c

任务:加入PTE

硬件部分

注意:一定要注意观察和区分,哪些地方用到的是虚拟地址,哪些地方是物理地址

任务:添加寄存器

  • 需要添加CR0CR3两个寄存器,其中CR0CR3的数据结构框架已帮我们定义好,在mmu.h中,你只需要添加就可以,这个头文件很重要,很多地方都需要引用它。
  • 注意初始化CR0寄存器
  • 添加操作CR0CR3的相关指令
  • 需要对CR0进行初始化

任务:修改vaddr_readvaddr_write函数

  • 先验证CR0paging位,如果没有开启,仍如修改前一样调用paddr_read/write直接传入虚拟地址获得/写入数据即可
  • 先不着急实现数据跨越两个页的情况
  • 开启了分页,于是调用page_translate函数得到转换好的物理地址

page_translate函数

  • 该函数用于地址转换,传入虚拟地址作为参数,函数返回值为物理地址
  • 该函数的实现过程即为我们理论课学到的页级转换过程(先找页目录项,然后取出)
  • 注意使用assert来验证present位,否则会造成调试困难
  • PDEPTE的数据结构框架已帮我们定义好,在mmu.h
  • 注意每个页目录想和每个页表项存储在内存中的地址均为物理地址,使用paddr_read去读取,如果使用vaddr_read去读取会造成死递归(为什么?)
  • 此外,还需要实现访问位和脏位的功能
  • 需要在page_translate中插入Log并截图表示实现成功(截图后可去除Log以免影响性能)
  • 如何编写这个函数?
  1. 根据CR3寄存器得到页目录表基址(是个物理地址)
  2. 用这个基址和从虚拟地址中隐含的页目录字段项结合计算出所需页目录项地址(是个物理地址)
  3. 从内存中读出这个页目录项,并对有效位进行检验
  4. 将取出的PDE和虚拟地址的页表字段相组合,得到所需页表项地址(是个物理地址)
  5. 从内存中读出这个页表项,并对有效位进行检验
  6. 检验PDEaccessed位,如果为0则需变为1,并写回到页目录项所在地址
  7. 检验PTEaccessed位如果为0,或者PTE的脏位为0且现在正在做写内存操作,满足这两个条件之一时需要将accessed位,然后更新dirty位,最后并写回到页表项所在地址
  8. 页级地址转换结束,返回转换结果(是个物理地址)

任务:需要实现跨页的时候再来实现

跨页的实现思路:

  1. 什么时候表示跨页了? 首先判断要读/写的长度和本页剩余的长度,若前者大则说明要读/写的数据跨页了
  2. 需要跨页时,首先读/写??的部分数据
  3. 然后计算新页的首地址,并将该地址转换为物理地址
  4. 对??的物理地址再做一次读/写操作
  5. 注意事项:如果是读跨页,则需组合两次读出的数据(位操作实现);如果是写跨页,应注意两个页分别需要写的数据长度是多少,以及写到两页上的内容分别是什么

软件部分

准备内核页表

  • 启动时,内存中什么有效数据都没有,我们需要定义宏HAS_PTE,这样一来,Nanos-lite就会在启动时初始化MM(已实现)了
  • 初始化MM的内容包括:设置空闲物理页的首地址,这样以后才可以通过调用new_page()来分配新的空闲物理页;填写内核中的页目录和页表,通过AM实现(已实现)
  • 注意:我们实现的是简易的分页机制,只分配就可以了,不需要回收

让用户程序运行在分页机制上

  • 到目前为止,用户程序总是被加载到0x04000000的地址上
  • 但是这个地址很低,且和内核的虚拟地址空间重合,内核可能在分配需要空间时破坏用户程序
  • 我们既然有虚拟地址了,就不再受空间限制,加载到任意的虚拟地址都是可以接受的(前提是不和内核冲突),只要做好到物理地址的映射就行

实现加载用户程序

任务:加载用户程序

  • 修改链接地址参数-Ttext0x8048000,为什么是这个地址?(和工业界一个巧合)
  • 修改loader的加载地址为DEFAULT_ENTRY,这样才能正确地将程序加载过去
  • 修改Nanos-litemain函数,不直接使用loader函数,而是调用load_proc函数来加载用户程序

理解load_proc函数

  1. 调用_protect创建用户进程的虚拟地址空间(其中用到的PCB叫做进程描述块,用于描述进程状态,有兴趣做PA4.2的同学到时候再了解),并将虚拟地址空间信息装入as字段中,每个进程有属于自己的页目录基地址,存放在ptr字段中
  2. 调用loader来加载程序
  3. 调用_switch切换到程序的虚拟地址空间
  4. 跳转到程序入口点
  5. 后面的操作和分时多任务有关,暂时不讲

任务:在分页机制上运行dummy

任务:实现_map函数

该函数用于将一页虚拟内存页映射到物理内存中,建立映射关系

  1. 从虚拟地址空间中获得页目录基地址
  2. 根据该页目录基地址和传入的虚拟地址选中一个页目录项
  3. 如果需要新的页表,使用palloc_f申请一张页表,并将其地址组合其他标志位,存入本个页目录项中
  4. 根据传入的虚拟地址选中一个页表项
  5. 将传入的物理地址组合其他标志位,形成一个页表项,存入选中的页表项

任务:修改loader函数

loader做如下修改:

  1. 打开待装入的文件后,还需要获取文件大小
  2. 需要循环判断是否已创建足够的页来装入程序
  3. 对于程序需要的每一页,做三个事情,即4,5,6步:
  4. 使用Nanos-liteMM提供的new_page()函数获取一个空闲物理页
  5. 使用映射函数_map()将本虚拟空间内当前正在处理的这个页和上一步申请到的空闲物理页建立映射
  6. 读一页内容,写到这个物理页上
  7. 每一页都处理完毕后,关闭文件,并返回程序入口点地址(虚拟地址)

任务:在分页机制上运行dummy

作为你成功实现分页的依据,你需要在loader()函数里面插入一个Log,插入到每次调用_map()函数前,通过Log()显示出每次程序调用_map()传入的第二个和第三个参数(vapa),代码如下:

void *pa = ???;
void *va = ???;
Log("Map va to pa: 0x%08x to 0x%08x", va, pa);
_map(???, va, pa);

参考输出的vapa每个人都可能会不同,但是总有一个正常范围,只要能正常运行dummy的一定是在正常范围内的,你的报告中一定要出现这个截图才能证明你正确实现了本节的功能

最终任务:在分页机制上运行仙剑奇侠传

任务:实现堆区管理

  • 我们之前总是认为sys_brk系统调用总是申请成功,因而总是返回0
  • 现在,我们要通过分页来真正实现堆区管理,首先令sys_brk调用mm_brk,并传入适当参数
  • MM中,实现堆区管理的过程如下(填充即可):
  • 通过一个循环将从current->max_brknew_brk为止的部分,以页为单位分别申请一页空闲页并分别建立映射

最终任务:在分页机制上运行仙剑奇侠传

PA4.1到此结束

本学期必做部分到此结束