OS-lab2

启动之后,我们的操作系统要能运行,需要一个载体,就是内存。内存的访问不是直接的,而是通过MMU完成。在完成MMU之前,我们先看看一些需要用到的宏定义和函数。

include

  • pmap.h

    定义了内存控制块Page结构体,用于记录页面的分配和使用情况。其中pp_link有两个成员,*le_next用于指向下一个元素,le_prev用于指向前一个元素的**le_next的地址。pp_ref用于记录页面被引用的次数。

    page2pa函数通过给定的Page控制块计算对应的页面的物理地址。

    page2ppn函数通过给定的Page控制块计算这个页面控制块在整个页面控制块数组中的下标。

    pa2page函数通过给定的物理地址计算对应的页面控制块。

    page2kva函数通过给定的Page控制块计算计算这个页面对应的内核虚地址。

    va2pa函数通过给定的页目录和虚拟地址计算对应的物理地址。

    此外还声明了pmap.c中的一些函数。

  • queue.h

    通过宏定义定义了关于链表的一些操作,如初始化、插入删除等。

  • mmu.h

    定义了一系列变量如页面大小BY2PG、标志位PTE_V等。PDX(va)用于获得虚拟地址的高10位,PTX(va)类似。PADDR(kva)用于将内核虚拟地址转化为物理地址,KADDR正好相反。PTE_ADDR用于获得页表项存储的物理地址。

现在,我们一步一步实现MMU。

mm

  • pmap.c

    这个文件集合了管理内存的大部分函数。其中*pages是页面控制块数组,freemem是空闲内存的地址。

    mips_detect_memory函数手动给basememmaxpanpage等全局变量进行了赋值。

    *alloc函数手动分配n字节的空闲内存。具体思路即修改freemem的值,然后将alooced_mem清零后返回这个地址。

    *boot_pgdir_walk函数用于获取对应虚拟地址对应的二级页表项。具体流程是通过页目录找到页目录项地址;将页目录项的值转为虚拟地址;检查是否存在,若不存在且create为1,就分配一页,并给对应的页表项赋上标志位;然后再求得对应二级页表项的虚拟地址并返回。

    boot_map_segment函数用于将size大小的虚拟地址区间映射到size大小的物理地址区间。首先将sizeBY2PG取整;然后以页为单位,利用*boot_pgdir_walk获得每一页的页表项,再将物理地址和对应的标志位赋给对应的页表项,完成映射。

    mips_vm_init函数完成初始的建立两级页表的任务。首先是用alloc函数分配一页给页目录;然后分配npagePagepages数组,用于管理页面;再将pages数组这片区域映射到物理内存;类似的,分配空间给envs进程控制块数组,并映射到对应的物理内存区域。

    以上都是在启动过程中完成的操作。核心是mips_vm_init函数。

    page_init函数用于初始化pages数组并设置page_free_list数组。首先用LIST_INIT函数初始化page_free_list;然后通过freememend求得已经分配了多少空闲内存;将已分配的内存即freemem以下的内存以页为单位,通过修改pp_ref为1的方式记录下来;剩下的内存即freemem以上的内存则将pp_ref记为0,并利用LIST_INSERT_HEAD加入到page_free_list中。

    page_alloc函数用于分配一页物理内存。首先需要检查是否存在空闲内存,即利用LIST_EMPTY检查page_free_list是否为空;然后取出第一项并在链表中删掉这一项;最后将这一项对应的内存清零后返回这一页面控制块。

    page_free函数用来释放一页内存。当pp_ref为0时将这一页利用LIST_INSERT_HEAD加入到page_free_list中。

    pgdir_walk函数类似boot_pgdir_walk函数,基本思路一样。区别在于,pgdir_walk函数是在启动之后,所以在需要创建新页表的时候可以使用page_alloc函数,并且分配后将对应的pp_ref增加。boot_pgdir_walk函数由于没有建立页表映射机制,对于pp_ref是无法操作的。

    page_insert函数用于将物理页映射到特定虚拟地址。首先利用pgdir_walk函数取得虚拟地址va的页表项;检查这一页表项是否已经对应到了指定页面上,若页表项不为空且已经映射到了指定页面,则利用tlb_invalidate更新tlb,然后给对应的页表项赋值并返回;若这一页表项不为空且映射的页面不是指定页面,则需要用page_remove将这一虚拟地址对应的页面移除;然后利用tlb_invalidate更新tlb;再一次利用pgdir_walk函数取得页表项,对这一页表项赋值并增加pp_ref

    page_lookup函数用于获得虚拟地址对应的Page和页表项。首先通过pgdir_walk得到虚拟地址对应的页表项;如果页表项为空或页表项有效位为0则返回;然后通过pa2page得到Page

    page_decref函数用于减少pp_ref

    page_remove函数用于移除物理页面到虚拟地址的映射。首先利用page_lookup获得Page和对应的页表项;减少pp_ref,若pp_ref为0则需要用page_free释放掉这一物理页;将页表项清零,并用tlb_invalidate更新tlb。

    tlb_invalidate函数用于使虚拟地址对应的tlb表项失效,而下次访问这个地址就会触发tlb充填,完成对tlb的更新。核心是调用tlb_out函数。

    剩下的三个函数physical_memory_manage_checkpage_checkpageout用于检查以上的内存管理函数功能是否正确。

  • tlb_asm.S

    这个文件用于处理tlb更新,即tlb_out函数。首先将CP0_ENTRYHI寄存器的值取出来,将虚拟地址写入;通过tlbp查找与CP0_ENTRYHI内容匹配的页表项并写入CP0_INDEX,由于tlb内部采用流水线设计,因此这条指令后需要执行nop;若找到则将CP0_ENTRYHICP0_ENTRYLO0清零,这样在未来对这一虚拟地址进行访问就会诱发tlb充填;利用tlbwi根据CP0_INDEX写tlb,然后恢复CP0_ENTRYHI;若没找到则将CP0_ENTRYHI恢复为原来的值并返回。

最新文章

  1. Codeforces 721E Road to Home
  2. [SQLServer大对象]——FileTable从文件系统迁移文件
  3. day1 基础总结
  4. Java清除:收尾和垃圾收集
  5. STM32F0xx_DAC输出电压配置详细过程
  6. OSSchedLock()函数透析
  7. 运行第一个Hadoop程序,WordCount
  8. Lucene.Net 2.3.1开发介绍——附录一、如何下载Lucene.Net的各种版本
  9. HTTP 返回状态值详解
  10. ThinkPhp知识大全(非常详细)
  11. 关于AJAX使用中出现中文乱码的问题
  12. tyvj/joyoi 1374 火车进出栈问题(水水版)
  13. 有关于Integer的一些小问题
  14. svn 提交数据
  15. java设计模式-----22、状态模式
  16. spring依赖注入之手工装配
  17. uva 10288 Coupons (分数模板)
  18. appium+python自动化57-chromedriver与chrome版本
  19. HDU-6341 Problem J. Let Sudoku Rotate(dfs 剪枝)
  20. spring的配置文件在web.xml中加载的方式

热门文章

  1. SQLSERVER 的 truncate 和 delete 有区别吗?
  2. 用ksweb+Android做服务器,搭建WordPress博客环境
  3. JAVA虚拟机21---JAVA内存模型
  4. Android JetPack~ DataBinding(数据绑定)(一) 集成与使用
  5. 3 .NET Core笔试题
  6. 浅拷贝导致的bug
  7. PostgreSQL数据库所有的等待事件
  8. Java中的static关键字作用及其应用
  9. LeetCode-1606 找到处理请求最多的服务器
  10. gmgo国密算法库