操作系统ucore lab2实验报告
2017-04-07 01:23
393 查看
练习0
填写已有实验本实验依赖实验1.请把要做的实验1的代码填入本实验中代码有lab1的注释相应部分
首先利用meld工具比较两个文件的差异
发现缺失的是
kdebug.c、trap.c两个文件的相关代码,补全后进行下一练习
首先运行
报错,看来就是需要进行实验的所有编程才能完整的运行
练习1
实现firstfit连续物理内存分配算法(需要编程)在实现firstfit内存分配算法的回首函数时,要考虑地址连续的空闲块之间的合并操作
在实验前先仔细学习一下firstfit算法
原理
要求空闲分区链以地址递增的次序链接。在分配内存时,从链首开始顺序查找,直至找到一个大小能满足要求的分区为止;然后再按照作业的大小,从该分取中划出一块内存空间分配给请求者,余下的空闲分区仍留在空闲链中。若从链首直到链尾都不能找到一个能满足要求的分区,则此次内存分配失败,返回。该算法倾向于优先利用内存中低地址部分的空闲分区,从而保留了高址部分的大空闲区。这给为以后到达的大作业分配大的内存空间创造了条件,其缺点是低址部分不断被划分,会留下许多难以利用的、很小的空闲分区,而每次查找又都是从低址部分开始,这无疑会增加查找可用空闲分区时的开销。大致流程图
为了与以后的分页机制配合,首先需要建立对整个计算机的每一个物理页的属性,用结构体Page来表示,它包含了映射此物理页的虚拟页个数,描述物理页属性的flags和双向链接各个Page结构的page_link双向链表。
struct Page{ int ref; uint32_t flags; unsigned int property; list_entry_t page_link; }
ref
表示该页被页表的引用记数。如果这个页被页表引用了,即在某页表中有一个页表项设置一个虚拟页到这个Page管理的物理页的映射关系,就会把Page的ref加一。反之,若页表项取消,即映射关系解除,就减一。
flags
表示此物理页的状态标记,有两种属性,bit 0表示是否被保留,如果被保留了则设为1,且不能放到空闲页链表中,即这样的页不是空闲页,不能动态分配与释放。比如内核代码占用的空间。bit 1表示此页是否是空闲的。如果设置为1,表示这页是空闲的,可以被分配;如果设置为0,表示这页已经被分配出去了,不能被再二次分配。
property
用来记录某连续内存空闲块的大小(即地址连续的空闲页的个数)。这里需要注意的是用到此成员变量的这个Page比较特殊,是连续内存空闲地址最小的一夜(即第一页)。
page_link
是便于把多个连续内存空闲块链接在一起的双向链表指针,连续内存空闲块利用这个页的成员变量page_link来链接比它地址小和大的其他连续内存空闲块
为了有效的管理这些小连续内存空闲块,所有的连续内存空闲块可用一个双向链表来管理,便于分配和释放,为此定义一个
free_area_t
typedef struct { list_entry_t free_list; // the list header unsigned int nr_free; // number of free pages in this free list } free_area_t;
free_list是一个list_entry结构的双向链表指针
nr_free则记录当前空闲页的个数
有了这两个数据结构,就可以管理起来整个以页尾单位的物理内存空间
理解完原理,开始进行实验
首先根据实验指导书,我们第一个实验需要完成的主要是
default_pmm.c中的
default_init,
default_init_memmap,
default_alloc_pages,
default_free_pages几个函数的修改。
[b]一些标志的定义:[/b]
default_init
static void default_init(void) { list_init(&free_list); nr_free = 0; }
看下注释
(2) default_init: you can reuse the demo default_init fun to init the free_list and set nr_free to 0.
free_list is used to record the free mem blocks. nr_free is the total number for free mem blocks.
根据注释代码已经完成无需改动
default_init_memmap
static void default_init_memmap(struct Page *base, size_t n) { assert(n > 0); struct Page *p = base; for (; p != base + n; p ++) { assert(PageReserved(p)); p->flags = p->property = 0; set_page_ref(p, 0); } base->property = n; SetPageProperty(base); nr_free += n; list_add(&free_list, &(base->page_link)); }
查看一下注释
* (3) default_init_memmap: CALL GRAPH: kern_init --> pmm_init-->page_init-->init_memmap--> pmm_manager->init_memmap * This fun is used to init a free block (with parameter: addr_base, page_number). * First you should init each page (in memlayout.h) in this free block, include: * 4000 p->flags should be set bit PG_property (means this page is valid. In pmm_init fun (in pmm.c), * the bit PG_reserved is setted in p->flags) * if this page is free and is not the first page of free block, p->property should be set to 0. * if this page is free and is the first page of free block, p->property should be set to total num of block. * p->ref should be 0, because now p is free and no reference. * We can use p->page_link to link this page to free_list, (such as: list_add_before(&free_list, &(p->page_link)); ) * Finally, we should sum the number of free mem block: nr_free+=n
相关定义
根据注释修改代码
static void default_init_memmap(struct Page *base, size_t n) { assert(n > 0); struct Page *p = base; for (; p != base + n; p ++) { assert(PageReserved(p));//判断是否是保留页,断言手法 p->flags = p->property = 0;//设置标志位 SetPageProperty(p);//设置bit set_page_ref(p, 0);//清空被引用的次数 list_add(&free_list, &(p->page_link));//将此页插入到空闲页的链表里面 } base->property = n;//连续内存空闲块的大小为n //SetPageProperty(base); nr_free += n;//说明有n个连续空闲块 //list_add(&free_list, &(base->page_link)); }
default_alloc_pages
此函数是用于为进程分配空闲页。其分配的步骤如下:- 寻找足够大的空闲块,如果找到了,重新设置标志位
- 从空闲链表中删除此页
- 判断空闲块大小是否合适 ,如果不合适,分割页块 ,如果合适则不进行操作
- 计算剩余空闲页个数
- 返回分配的页块地址
static struct Page * default_alloc_pages(size_t n) { assert(n > 0); if (n > nr_free) { return NULL; } struct Page *page = NULL; list_entry_t *le = &free_list; while ((le = list_next(le)) != &free_list) { struct Page *p = le2page(le, page_link); if (p->property >= n) { page = p; break; } } if (page != NULL) { list_del(&(page->page_link)); if (page->property > n) { struct Page *p = page + n; p->property = page->property - n; list_add(&free_list, &(p->page_link)); } nr_free -= n; ClearPageProperty(page); } return page; }
查看一下注释
* (4) default_alloc_pages: search find a first free block (block size >=n) in free list and reszie the free block, return the addr * of malloced block. * (4.1) So you should search freelist like this: * list_entry_t le = &free_list; * while((le=list_next(le)) != &free_list) { * .... * (4.1.1) In while loop, get the struct page and check the p->property (record the num of free block) >=n? * struct Page *p = le2page(le, page_link); * if(p->property >= n){ ... * (4.1.2) If we find this p, then it means we find a free block(block size >=n), and the first n pages can be malloced. * Some flag bits of this page should be setted: PG_reserved =1, PG_property =0 * unlink the pages from free_list * (4.1.2.1) If (p->property >n), we should re-caluclate number of the the rest of this free block, * (such as: le2page(le,page_link))->property = p->property - n;) * (4.1.3) re-caluclate nr_free (number of the the rest of all free block) * (4.1.4) return p * (4.2) If we can not find a free block (block size >=n), then return NULL
相关定义
根据注释修改代码
static struct Page * default_alloc_pages(size_t n) { assert(n > 0); if (n > nr_free) {//当空闲页不够时,返回NULL return NULL; } list_entry_t *le = &free_list; while ((le = list_next(le)) != &free_list) {//遍历所有指针 struct Page *p = le2page(le, page_link);//转换为页结构 if (p->property >= n) {//如果找到空闲页大小大于等于n时选中 int i; list_entry_t *len; for(i = 0; i < n; i++)//初始化分配内存 { len = list_next(le); struct Page *p2 = le2page(temp_le, page_link); //转换页结构 SetPageReserved(p2); //初始化标志位 ClearPageProperty(p2); list_del(le); //清除双向链表指针 le = len; } if(p->property > n) { //若大小>n,只取大小为n的块 (le2page(le,page_link))->property = p->property - n; } ClearPageProperty(p); //初始化标志位 SetPageReserved(p); nr_free -= n; //空闲页大小-n return p; } } return NULL;//分配失败 }
default_free_pages
这个函数的作用是释放已经使用完的页,把他们合并到free_list中。 具体步骤如下:
- 在
free_list中查找合适的位置以供插入
- 改变被释放页的标志位,以及头部的计数器
- 尝试在
free_list中向高地址或低地址合并
static void default_free_pages(struct Page *base, size_t n) { assert(n > 0); struct Page *p = base; for (; p != base + n; p ++) { assert(!PageReserved(p) && !PageProperty(p)); p->flags = 0; set_page_ref(p, 0); } base->property = n; SetPageProperty(base); list_entry_t *le = list_next(&free_list); while (le != &free_list) { p = le2page(le, page_link); le = list_next(le); if (base + base->property == p) { base->property += p->property; ClearPageProperty(p); list_del(&(p->page_link)); } else if (p + p->property == base) { p->property += base->property; ClearPageProperty(base); base = p; list_del(&(p->page_link)); } } nr_free += n; list_add(&free_list, &(base->page_link)); }
查看注释
* (5) default_free_pages: relink the pages into free list, maybe merge small free blocks into big free blocks. * (5.1) according the base addr of withdrawed blocks, search free list, find the correct position * (from low to high addr), and insert the pages. (may use list_next, le2page, list_add_before) * (5.2) reset the fields of pages, such as p->ref, p->flags (PageProperty) * (5.3) try to merge low addr or high addr blocks. Notice: should change some pages\'s p->property correctly.
相关定义
根据注释修改代码
static void default_free_pages(struct Page *base, size_t n) { assert(n > 0); assert(PageReserved(base)); list_entry_t *le = &free_list;//找合适的位置 struct Page *p = base; while((le=list_next(le)) != &free_list) { p = le2page(le, page_link); if(p > base) { break; } } for(p = base; p < base + n; p++)//在之前插入n个空闲页 { list_add_before(le, &(p->page_link)); p->flags = 0;//设置标志 set_page_ref(p, 0); ClearPageProperty(p); SetPageProperty(p); } base->property = n;//设置连续大小为n //如果是高位,则向高地址合并 p = le2page(le,page_link); if(base + base->property == p ) { base->property += p->property; p->property = 0; } //如果是低位且在范围内,则向低地址合并 le = list_prev(&(base->page_link)); p = le2page(le, page_link); if(le != &free_list && p == base-1)//满足条件,未分配则合并 { while(le != &free_list) { if(p->property)//当连续时 { p->property += base->property; base->property = 0; break; } le = list_prev(le); p = le2page(le,page_link); } } nr_free += n; }
练习2
实现寻找虚拟地址对应的页表项(需要编程)通过设置页表和对应的页表项,可建立虚拟内存地址和物理内存地址的对应关系。其中的get_pte函数是设置页表项缓解中的一个重要步骤。此函数找到一个虚地址对应的二级页表项的内核虚地址,如果此二级页表项不存在,则分配一个包含此项的二级页表。
相关定义
PDX(la): 返回虚拟地址la的页目录索引
KADDR(pa): 返回物理地址pa相关的内核虚拟地址
set_page_ref(page,1): 设置此页被引用一次
page2pa(page): 得到page管理的那一页的物理地址
struct Page * alloc_page() : 分配一页出来
memset(void * s, char c, size_t n) : 设置s指向地址的前面n个字节为字节‘c’
PTE_P 0x001 表示物理内存页存在
PTE_W 0x002 表示物理内存页内容可写
PTE_U 0x004 表示可以读取对应地址的物理内存页内容
根据注释完成代码
//get_pte - get pte and return the kernel virtual address of this pte for la // - if the PT contians this pte didn't exist, alloc a page for PT // parameter: // pgdir: the kernel virtual base address of PDT // la: the linear address need to map // create: a logical value to decide if alloc a page for PT // return vaule: the kernel virtual address of this pte pte_t * get_pte(pde_t *pgdir, uintptr_t la, bool create) { /* LAB2 EXERCISE 2: YOUR CODE * * If you need to visit a physical address, please use KADDR() * please read pmm.h for useful macros * * Maybe you want help comment, BELOW comments can help you finish the code * * Some Useful MACROs and DEFINEs, you can use them in below implementation. * MACROs or Functions: * PDX(la) = the index of page directory entry of VIRTUAL ADDRESS la. * KADDR(pa) : takes a physical address and returns the corresponding kernel virtual address. * set_page_ref(page,1) : means the page be referenced by one time * page2pa(page): get the physical address of memory which this (struct Page *) page manages * struct Page * alloc_page() : allocation a page * memset(void *s, char c, size_t n) : sets the first n bytes of the memory area pointed by s * to the specified value c. * DEFINEs: * PTE_P 0x001 // page table/directory entry flags bit : Present * PTE_W 0x002 // page table/directory entry flags bit : Writeable * PTE_U 0x004 // page table/directory entry flags bit : User can access */ //typedef uintptr_t pde_t; pde_t *pdep = &pgdir[PDX(la)]; // (1)获取页表 if (!(*pdep & PTE_P)) // (2)假设页目录项不存在 { struct Page *page; if (!create || (page = alloc_page()) == NULL) // (3) check if creating is needed, then alloc page for page table { //假如不需要分配或是分配失败 return NULL; } set_page_ref(page, 1); // (4)设置被引用1次 uintptr_t pa = page2pa(page); // (5)得到该页物理地址 memset(KADDR(pa), 0, PGSIZE); // (6)物理地址转虚拟地址,并初始化 *pdep = pa | PTE_U | PTE_W | PTE_P; // (7)设置可读,可写,存在位 } return &((pte_t *)KADDR(PDE_ADDR(*pdep)))[PTX(la)]; // (8) return page table entry //KADDR(PDE_ADDR(*pdep)):这部分是由页目录项地址得到关联的页表物理地址,再转成虚拟地址 //PTX(la):返回虚拟地址la的页表项索引 //最后返回的是虚拟地址la对应的页表项入口地址 }
练习3
释放某虚拟地址所在的页并取消对应的二级页表项的映射(需要编程)当释放一个包含某虚地址的物理内存页时,需要让对应此物理内存页的管理数据结构Page做相关的清除处理,使得次物理内存页成为空闲;另外还需把表示虚地址与物理地址对应关系的二级页表项清除
相关定义
struct Page *page pte2page(*ptep):得到页表项对应的那一页
free_page : 释放一页
page_ref_dec(page) : 减少该页的引用次数,返回剩下引用次数
tlb_invalidate(pde_t * pgdir, uintptr_t la) : 当修改的页表是进程正在使用的那些页表,使之无效
//page_remove_pte - free an Page sturct which is related linear address la // - and clean(invalidate) pte which is related linear address la //note: PT is changed, so the TLB need to be invalidate static inline void page_remove_pte(pde_t *pgdir, uintptr_t la, pte_t *ptep) { /* LAB2 EXERCISE 3: YOUR CODE * * Please check if ptep is valid, and tlb must be manually updated if mapping is updated * * Maybe you want help comment, BELOW comments can help you finish the code * * Some Useful MACROs and DEFINEs, you can use them in below implementation. * MACROs or Functions: * struct Page *page pte2page(*ptep): get the according page from the value of a ptep * free_page : free a page * page_ref_dec(page) : decrease page->ref. NOTICE: ff page->ref == 0 , then this page should be free. * tlb_invalidate(pde_t *pgdir, uintptr_t la) : Invalidate a TLB entry, but only if the page tables being * edited are the ones currently in use by the processor. * DEFINEs: * PTE_P 0x001 // page table/directory entry flags bit : Present */ if (*ptep & PTE_P) //(1) check if this page table entry is present { //假如页表项存在 struct Page *page = pte2page(*ptep);//(2)找到页表项的那一页信息 if (page_ref_dec(page) == 0)//(3)如果没有被引用 { free_page(page);//(4)释放该页 } *ptep = 0; //(5)该页目录项清零 tlb_invalidate(pgdir, la); //(6) flush tlb当修改的页表是进程正在使用的那些页表,使之无效 } }
运行结果
收获
通过本次实验,我了解如何发现系统中的物理内存,了解如何建立对物理内存的初步管理,了解了页表的相关的操作,即如何建立页表来实现虚拟内存到物理内存之间的映射,对段页式内存管理机制有一个比较全面的了解。基本上在试验中学习,根据注释以及函数定义可以动手完成一个简单的物理内存管理系统。完成后发现运行错误,通过一遍一遍的核对代码,查阅资料,比对正确答案,终于得以修改,成功运行。相关文章推荐
- 操作系统ucore lab2实验报告
- 操作系统课程实验报告(四)
- ucore lab2 实验报告
- ucore操作系统lab2实验报告
- 操作系统实验报告 lab6
- 操作系统实验报告 lab1
- 计算机操作系统实验之_进程观测_实验报告
- 中山大学软件工程操作系统实验一报告
- 操作系统课程实验报告(三)
- 计算机操作系统实验之_进程观测_实验报告
- 操作系统实验报告-地址映射
- 操作系统ucore lab1实验报告
- 操作系统实验报告:ucore_lab2~5
- “Linux内核分析”实验报告(七)Linux 操作系统如何装载链接并执行程序
- 操作系统实验报告 lab7
- ucore操作系统lab4实验报告(理论部分)
- # 操作系统实验报告:ucore-lab1
- 操作系统实验报告 lab4
- 操作系统文件系统设计实验报告
- 操作系统实验报告:ucore-lab1