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

posted @ 2020-04-17 01:18  -Limbo-  阅读(368)  评论(0编辑  收藏  举报