您的位置:首页 > 其它

操作系统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当修改的页表是进程正在使用的那些页表,使之无效
}

}


运行结果





收获

  通过本次实验,我了解如何发现系统中的物理内存,了解如何建立对物理内存的初步管理,了解了页表的相关的操作,即如何建立页表来实现虚拟内存到物理内存之间的映射,对段页式内存管理机制有一个比较全面的了解。基本上在试验中学习,根据注释以及函数定义可以动手完成一个简单的物理内存管理系统。完成后发现运行错误,通过一遍一遍的核对代码,查阅资料,比对正确答案,终于得以修改,成功运行。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息