操作系統ucore lab2實驗報告

練習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_initdefault_init_memmapdefault_alloc_pagesdefault_free_pages幾個函數的修改。

一些標誌的定義:

這裏寫圖片描述

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:
 *                  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當修改的頁表是進程正在使用的那些頁表,使之無效
    }

}

運行結果

這裏寫圖片描述

這裏寫圖片描述

收穫

  通過本次實驗,我瞭解如何發現系統中的物理內存,瞭解如何建立對物理內存的初步管理,瞭解了頁表的相關的操作,即如何建立頁表來實現虛擬內存到物理內存之間的映射,對段頁式內存管理機制有一個比較全面的瞭解。基本上在試驗中學習,根據註釋以及函數定義可以動手完成一個簡單的物理內存管理系統。完成後發現運行錯誤,通過一遍一遍的核對代碼,查閱資料,比對正確答案,終於得以修改,成功運行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章