• OS Lab2 Code Review


    Lab2代码阅读

    wzk

     

    本次文件

    queue.h

    mmu.h

    pmap.h

    pmap.c

    tlb_asm.S

     

    queue.h

    主要定义了链表的操作,最后两个宏函数是尾队列相关

    LIST_HEAD:定义一个指针head指向链表表头

    LIST_HEAD_INITIALIZER:通过将head赋值为此来将链表变为空

    LIST_ENTRY:定义链表项,包含type *le_next和**le_prev。注意与双向链表不同的是,这里的le_prev指向前一个元素的le_next指针。

    LIST_EMPTY:判定链表是否为空

    LIST_FIRST:返回head指向的链表的表头指针

    LIST_FOREACH:封装了一个for(;;)语句,用于遍历链表

    LIST_INIT:直接将链表head变为为空

    LIST_INSERT_AFTER:(作业)在listelm元素后插入elm,注意le_prev指向的是指针

    LIST_INSERT_BEFORE:在listelm前插入elm

    LIST_INSERT_HEAD:在表头插入elm

    LIST_INSERT_TAIL:在表尾插入elm

    LIST_NEXT:返回元素的next指针

    LIST_REMOVE:删除链表中的elm

    TAILQ_HEAD:定义尾队列的head指针(*tqh_first、**tqh_las)

    TAILQ_ENTRY:定义尾队列中的元素(类似LIST_ENTRY)

     

    mmu.h

    第一部分为MIPS定义。

    BY2PG:每页字节数(4KB)

    PDMAP:每个页目录项映射的字节大小(4MB)

    PGSHIFT: 虚拟地址中二级页表号的右移位数(12位)

    PDSHIFT:虚拟地址中页目录号的右移位数(22位)

    PDX:对虚拟地址取页目录号的宏(31~22位)

    PTX:对虚拟地址取页表号的宏(21~12位)

    PTE_ADDR:取页表项的对应地址(清空低12位的标识位)

    PPN:物理页号(右移12位)

    VPN:虚拟页号(与PPN相同)

    VA2PFN:虚拟地址转换为page frame number(清空低12位)

    PTE_G~PTE_LIBRARY 页表的标识位(有效位、dirty bit、uncached等)


    MIPS内存映射布局图解(kuseg,kseg0~kseg3)

    KERNBASE:内核内存起始地址

    VPT:

    KSTACKTOP:内核栈顶地址

    KSTKSIZE:内核栈大小

    ULIM:kuseg的地址上界

    UVPT:用户VPT地址

    UPAGES:用户页地址

    UENVS:用户进程地址

    UTOP:用户栈顶(比USTACKTOP多了异常栈)

    UXSTACKTOP:同上

    TIMESTACK:

    USTACKTOP:用户正常栈栈顶

    UTEXT:用户代码段

    E_UNSPECIFIED~E_NOT_EXEC:用于函数返回值的各种异常编码(未知、进程不存在、无效参数、内存不足、无进程空间,文件系统的磁盘无空间、打开文件过多、文件未找到、错误路径、文件已存在、文件不可执行、最大异常编码)


    bcopy:将src对应大小的内容复制到dst(定义于init.c)

    bzero:清空地址对应大小空间的内容(定义于init.c)

    bootstacktop,bootstack:

    npage:页数量(在pmap.c)

    Pde:页目录项类型(u_long)

    Pte:页表项类型(u_long)

    vpt:虚拟页表指针数组(Pte* [])

    vpd:虚拟页目录指针数组(Pde* [])

    PADDR:将核虚拟地址(kva)转换为物理地址,若在kuseg则报错,否则清除最高位

    KADDR:PADDR的逆运算将物理地址转化为核虚拟地址,若对应页号(右移12位)大于页表数量则报错,否则最高位赋1

    assert:断言,参数为0时panic

    TRUP:那就是返回参数和ULIM的最大值(ULIM转换为参数的类型)

    tlb_out:定义在汇编tlb_asm.S里

     

    pmap.h

    作为pmap.c的头文件出现

    Page_list:元素类型为Page的链表Page_list

    Page_LIST_entry_t:元素为Page的链表的项(包含*le_next和**le_prev,指向前后元素)类型

    Page:由pp_link(一个Page链表项)和pp_ref(页面引用次数)构成的结构体

    Page_list实质为Page的链表,Page包含pp_ref和链表项(前后指针)

    pages:若干个页的数组(指针,相当于页起始地址)

    page2ppn:将页指针转化为物理页号(减去pages)

    page2pa:将页指针转化为物理地址(转化为物理页号再左移PGSHIFT位)

    pa2page:将物理地址转化为页指针,若页号超过页表总数量则报错

    page2kva:将页指针转化为内核虚拟地址(先page2pa转化为物理地址再KADDR)

    va2pa:将虚拟地址转化为物理地址(通过二级页表),若对应页目录项/页表项无效则返回非0,返回的是页的起始地址

    声明了pmap.c里的各种函数和mips_init,方便使用

     

    tlb_asm.S

    定义了tlb_out函数,功能是清除虚拟地址(参数)对应的TLB项。具体流程如下:

    首先将a0(参数,虚拟地址)写入ENTRYHI,然后使用tlbp设置Index为a0对应的TLB项,若Index小于0(未找到)则跳转到NOFOUND,否则将TLB[Index]设为0->0的映射(通过将ENTRYHI和ENTRYLO0设为0实现)

    tlbp是寻找与a0匹配的TLB入口,加载至Index

    tlbwi是将ENTRYHI和ENTRYLO0对应的映射关系(页表项)写入TLB[index]

    4行nop是为了保证CPU在没有转发机制时,tlbp和mfc0接连用到Index寄存器不会触发数据冒险

     

    pmap.c

    包含物理内存与虚拟内存管理的相关函数,完成了页表初始化、插入、查找、删除等一系列操作

    开始时全局变量:最大物理地址maxpa(相对于ULIM)、内存最大页数npage、基础内存basemem、扩展内存extmem、空闲内存地址freemem、页指针(数组)pages、空闲链表page_free_list、页目录基地址boot_pdgir。除后三个其余均为u_long类型

    void mips_detec_memory()

    初始化部分全局变量(手动赋值)并打印

    maxpa=basemem=0x4000 0000,extmem=0x0,npage=basemem>>PGSHIFT

    void *alloc(u_int n, u_int align, int clear)

    分配n字节物理内存并对齐,clear为1时清空对应内存区域。只在初始化虚拟内存时使用。内存不足时panic,否则内存返回地址

    首先从linker script(定义data bss text段的文件)中获得end,表示当前空闲内存的起始地址(已分配内存的结束地址),将freemem初始化为end

    将freemem对align对齐,增加n字节,在clear=1时调用bzero清空内存,若freemem对应物理地址超过最大物理地址则报错,否则返回分配的内存的低地址

    freemem由end初始化,故为虚拟地址

    Pte *boot_pgdir_walk(Pde *pgdir, u_long va, int create)

    返回虚拟地址va对应的二级页表项(基于pgdir对应的页目录),若不存在页表项且create=1则创建。

    首先使用pgdir+页目录号PDX(va)得到页目录项,然后PTE_ADDR清空标识位、KADDR转化为内核虚拟地址,得到对应页表

    若页目录项标识位为0(页表不存在),则create=1时给pgtable分配一页内存,页目录项赋值为PADDR(pgtable),增加有效位PTE_V和dirty bit PTE_R否则返回0

    最后二级页表基地址pgtable+二级页表号PTX(va)得到页表项并返回

    页目录项指针pgdir_entryp为物理地址,页表项指针pgtable为虚拟地址

    void boot_map_segment(Pde *pgdir, u_long va, u_long size, u_long pa, int perm)

    将虚拟地址[va,va+size]映射到物理地址[pa,pa+size],pgdir为页目录基地址,perm作为有效位

    检查size是否为BY2PG整数倍,不满足则返回(ROUND也可以)

    循环遍历va到va+size,调用boot_pgdir_walk返回va+i对应的页表项指针,将页表项赋值为pa+i,低12位清空并加上有效位perm|PTE_V以及dirty bit PTE_R

    void mips_vm_init()

    初始化虚拟内存,建立二级页表

    首先在freemem给页目录分配一页空间,设定mCONTEXT(asm中定义)为页目录基地址

    然后初始化物理内存,分配npage个一页大小的内存给pages,调用boot_map_segment将用户页起始地址UPAGES开始的npage页数(向BY2PG对齐)映射到pages物理页对应的物理地址

    最后初始化进程控制块envs,方式与物理页相同,从用户进程空间UENVS开始映射

     


    上述函数调用关系:mips_vm_init调用alloc和boot_map_segment,boot_map_segment调用boot_pgdir_walk,


    以下page_alloc和page_free为物理内存的管理

     

    void page_init()

    初始化空闲链表和虚拟页。每个页的pp_ref表示引用次数,空闲页存在空闲链表中

    首先调用LIST_INIT初始化空闲链表,将freemem对齐到BY2PG。

    然后将物理页数组pages的PADDR(freemem)/BY2PG以下部分的pp_ref赋为1,以上部分的pp_ref赋为0并插入空闲链表头部

    int page_alloc(struct Page **pp)

    从空闲链表中分配一个物理页到*pp,返回值表示是否成功

    若空闲链表为空则返回错误,否则取出空闲链表第一项并从链表中移除,调用bzero将对应虚拟地址的空间清零,赋给*pp,返回0

    void page_free(struct Page *pp)

    释放一页,标记为空闲

    若pp_ref>0则返回,=0则插入空闲链表头部,<0则报错

    int pgdir_walk(Pde *pgdir, u_long va, int create, Pte **ppte)

    类似boot_pgdir_walk,设置*ppte为虚拟地址va对应的二级页表项(基于pgdir对应的页目录),若不存在页表项且create=1则创建。

    首先使用pgdir+页目录号PDX(va)得到页目录项,然后PTE_ADDR清空标识位、KADDR转化为内核虚拟地址,得到对应页表

    若页目录项标识位为0(页表不存在),则create=1时给pgtable分配一页内存(使用page_alloc,与boot_pgdir_walk不同),没有空闲页则返回错误,否则对应页的pp_ref+=1,页目录项赋值为PADDR(pgtable),增加有效位PTE_V和dirty bit PTE_R否则返回0

    最后二级页表基地址pgtable+二级页表号PTX(va)得到页表项并赋值给*ppte,返回0

    页目录项指针pgdir_entryp为物理地址,页表项指针pgtable为虚拟地址

    int page_insert(Pde *pgdir, struct Page *pp, u_long va, u_int perm)

    将物理页pp映射到虚拟地址va

    首先调用pgdir_walk(create为0)查找va对应的二级页表项

    若页表项不为0(va已有对应页)且有效,检查其对应地址的对应页是否为pp,是则更新TLB并更新有效位返回0,否则去除原有页

    然后更新TLB,重新调用pgdir_walk(create为1)创建va对应页,无空闲页则报错,否则插入页(将对应页表项赋值为物理页pp对应的物理地址),标记许可位,pp_ref+=1,返回0

    struct Page *page_lookup(Pde *pgdir, u_long va, Pte **ppte)

    查找va对应的物理页,返回对应页,对应该页的页表项存入*ppte

    调用pgdir_walk查找页表项存入pte,若页表项不存在(pte对应0)或无效(pte对应标识位为0)则返回0

    否则将pte存入*ppte,pte对应物理地址转化为物理页并返回

    void page_decref(struct Page *pp)

    将pp对应页的pp_ref减1,减至0时释放此页(调用page_free插入空闲链表)

    void page_remove(Pde *pgdir, u_long va)

    将va对应的物理页移除

    调用page_lookup查找va对应页,返回0(错误)则返回,否则pp_ref-1,减至0时移至空闲链表

    将页表项对应值清0,更新TLB

    void tlb_invalidate(Pde *pgdir, u_long va)

    更新TLB,删除va对应的TLB项

    将va低12位清零,若有进程则与进程ID做或运算,然后调用汇编tlb_asm.S中的tlb_out

    void physical_memory_manage_check()

    物理内存管理检验

    void page_check()

    虚拟内存管理检验

    void pageout(int va, int context)

    进程切换时更换页,context作为页目录基地址

    若context在用户区则报TLB回填错误,若va在进程区或小于0x10000则报错,若page_alloc失败则报错

    page_alloc的页的ppref加1,在context基地址插入页p,虚拟地址为va对应的page frame number

     

    pageout调用page_insert和page_alloc,page_insert调用pgdir_walk,pgdir_walk调用page_alloc,page_remove调用page_lookup,page_lookup调用pg_dir_walk,page_decref调用page_free

  • 相关阅读:
    ansible笔记(11):初识ansible playbook(二)
    Linux下查看占用CPU与内存最高的进程
    ansible笔记(10):初识ansible playbook
    AbpZero Http 模式下 Chrome浏览器因Cookie 不能登录
    Tomcat 8443&8080 并存
    接入腾讯cos文件存储
    安卓包打渠道标签
    java Android与PHP encode的区别
    thinkphp常用
    phalcon task任务
  • 原文地址:https://www.cnblogs.com/zkwang/p/12717117.html
Copyright © 2020-2023  润新知