MIT6.828 Fall2018 筆記 - Lab 2: Memory Management


Lab 2: Memory Management

建議先看完xv6book的Chapter1和Chapter2

Introduction

Memory management有兩部分:physical memory allocator for the kernel和virtual memory。

The x86 hardware's memory management unit (MMU) performs the mapping when instructions use memory, consulting a set of page tables.

Getting started

git pull
git checkout -b lab2 origin/lab2
git merge lab1
# 如果有沖突就處理一下
# git add .
# git commit

memlayout.h描述了virtual address space的布局,你需要通過修改pmap.c來實現virtual address space。你會使用PageInfo結構體來跟蹤空閑的和已經被分配的physical memory page。kclock.ckclock.h控制電池供電時鍾和CMOS RAM硬件。BIOS記錄了PC包含的物理內存量。pmap.c的代碼為了弄清有多少物理內存需要讀取硬件設備。這個實驗需要理解memlayout.h pmap.h inc/mmu.h中的定義。

Part 1: Physical Page Management

進入kernel后,在i386_init函數中調用mem_init函數,初始化kernel的內存。

Execrise 1

補全以下函數:

boot_alloc()
mem_init() (到check_page_free_list(1);處為止)
page_init()
page_alloc()
page_free()

check_page_free_list() 和 check_page_alloc() 會測試你的 physical page allocator.

  1. boot_alloc()
static void *
boot_alloc(uint32_t n)
{
    static char *nextfree;	// 下一個空閑內存的首字節虛擬地址
    char *result;

    // end是linker自動生成的,指向bss段的尾部
    // 使用objdump -h obj/kern/kernel查看一下各段的地址
    // 可知.bss就是kernel文件末尾的一個段
    if (!nextfree) {
        extern char end[];
        nextfree = ROUNDUP((char *) end, PGSIZE);
    }

    // LAB 2: Your code here.
    if (n == 0) {
        return nextfree;
    }
    result = nextfree;
    nextfree += ROUNDUP(n, PGSIZE);
    return result;
}
  1. mem_init()
void
mem_init(void)
{
    uint32_t cr0;
    size_t n;
    // 查明這個機器有多少內存
    i386_detect_memory();
    //panic("mem_init: This function is not finished\n");注釋掉這個
    // 創建初始頁目錄,並初始化為0
    kern_pgdir = (pde_t *) boot_alloc(PGSIZE);
    memset(kern_pgdir, 0, PGSIZE);
    // Permissions: kernel R, user R. 目前不需要理解這段
    kern_pgdir[PDX(UVPT)] = PADDR(kern_pgdir) | PTE_U | PTE_P;

    // 分配一個PageInfo數組,共npages個PageInfo,並初始化為0
    pages = (struct PageInfo*)boot_alloc(npages * sizeof(struct PageInfo));
    memset(pages, 0, npages * sizeof(struct PageInfo));
---省略---
  1. page_init()
// 初始化 page structure 和 內存free表
void page_init(void) {
    // The example code here marks all physical pages as free.
    // However this is not truly the case.  What memory is free?
    size_t i;
    //  1) Mark physical page 0 as in use.
    //     This way we preserve the real-mode IDT and BIOS structures
    //     in case we ever need them.  (Currently we don't, but...)
    pages[0].pp_ref = 1; // 代表已被使用
    //  2) The rest of base memory, [PGSIZE, npages_basemem * PGSIZE)
    //     is free.
    for (i = 1; i < npages_basemem; i++) {
        pages[i].pp_ref = 0;
        pages[i].pp_link = page_free_list;
        page_free_list = &pages[i];
    }
    //  3) Then comes the IO hole [IOPHYSMEM, EXTPHYSMEM), which must
    //     never be allocated.
    for (i = (IOPHYSMEM >> PGSHIFT); i < (EXTPHYSMEM >> PGSHIFT); i++) {
        pages[i].pp_ref = 1;
    }
    //  4) Then extended memory [EXTPHYSMEM, ...).
    //     Some of it is in use, some is free. Where is the kernel
    //     in physical memory?  Which pages are already in use for
    //     page tables and other data structures?
    //
    size_t first_free_page = (PADDR(boot_alloc(0))) >> PGSHIFT;
    for (i = (EXTPHYSMEM >> PGSHIFT); i < first_free_page; i++) {
        pages[i].pp_ref = 1;
    }

    // Change the code to reflect this.
    // NB: DO NOT actually touch the physical memory corresponding to
    // free pages!
    for (i = first_free_page; i < npages; i++) {
        pages[i].pp_ref = 0;
        pages[i].pp_link = page_free_list;
        // page_free_list指向表頭
        page_free_list = &pages[i];
    }
}
  1. page_alloc()
struct PageInfo* page_alloc(int alloc_flags) {
    if (page_free_list == NULL) {
        return NULL;
    }
    struct PageInfo* result = page_free_list;
    page_free_list = result->pp_link;
    result->pp_link = NULL;
    if (alloc_flags && ALLOC_ZERO) {
        memset(page2kva(result), 0, PGSIZE);
    }
    // Fill this function in
    return result;
}
  1. page_free()
void
page_free(struct PageInfo *pp)
{
    // Fill this function in
    // Hint: You may want to panic if pp->pp_ref is nonzero or
    // pp->pp_link is not NULL.
    if (pp->pp_ref != 0 || pp->pp_link != NULL) {
        panic("page_free failed. pp->pp_ref is nonzero or pp->pp_link is not NULL!");
        return;
    }
    pp->pp_link = page_free_list;
    page_free_list = pp;
}

然后運行JOS,輸出一下內容則說明你寫對了:

check_page_free_list() succeeded!
check_page_alloc() succeeded!

Part 2: Virtual Memory

先了解 x86's protected-mode memory management architecture: namely segmentation and page translation、邏輯地址、線性地址、物理地址

請看嚶特爾的 Intel® 64 and IA-32 Architectures Software Developer's Manual Combined Volumes 3A, 3B, 3C, and 3D: System Programming Guide 里面的第三章

分段和分頁的過程見Figure 3-1. Segmentation and Paging

Exercise 3

make qemu后,在終端里Ctrl-a c進入 QEMU monitor。使用xpx命令可以查看物理地址和虛擬地址的內存內容,info tlb可以查看page tables的信息(我的版本是4.2.0,如果用的是mit的patched版本,則是info pg),info mem可以查看哪個范圍的虛擬地址被map。

Reference counting

一些物理頁面同時被多個虛擬頁面映射,struct PageInfo 的 pp_ref 就是引用計數,當這個數值為0時,就可以將其加入free list

Exercise 4

實現以下函數:

pgdir_walk()
boot_map_region()
page_lookup()
page_remove()
page_insert()

實現可以參考xv6,嚶特爾開發人員手冊卷三 Figure 4-2 Figure 4-4 Table 4-5

  1. pgdir_walk()
pte_t* pgdir_walk(pde_t* pgdir, const void* va, int create) {
    // pde 即 page directory entry
    pde_t* pde = &pgdir[PDX(va)];
    pte_t* pgtab; // 指向pg table的指針
    if (*pde & PTE_P) {
        // 先獲取page table的地址
        pgtab = KADDR(PTE_ADDR(*pde));
    } else { // 如果相關page table不存在,就考慮alloc一個pgtab
        // 如果不創建那就直接返回NULL
        if (!create) {
            return NULL;
        }
        // 創建時將頁面內容置0
        struct PageInfo* pp = page_alloc(ALLOC_ZERO);
        if (pp == NULL) {
            return NULL;
        }
        pp->pp_ref++;
        pgtab = (pte_t*)page2kva(pp);
        *pde = PADDR(pgtab) | PTE_P | PTE_W | PTE_U;
    }
    return &pgtab[PTX(va)];
}
  1. boot_map_region()
static void boot_map_region(pde_t* pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm) {
    pte_t* pte;
    uintptr_t last = ROUNDDOWN(va + size - 1, PGSIZE);
    va = ROUNDDOWN(va, PGSIZE);
    pa = ROUNDDOWN(pa, PGSIZE);
    for (;;) {
        if ((pte = pgdir_walk(pgdir, (void*)va, 1)) == NULL) {
            return;
        }
        *pte = pa | perm | PTE_P;
        if (va == last) {
            return;
        }
        va += PGSIZE;
        pa += PGSIZE;
    }
}
  1. page_lookup()
struct PageInfo* page_lookup(pde_t* pgdir, void* va, pte_t** pte_store) {
    pte_t* pte;
    if ((pte = pgdir_walk(pgdir, va, 0)) == NULL) {
        return NULL;
    }
    // pte_store 是指向一個 pte 的指針
    if (pte_store) {
        *pte_store = pte;
    }
    // Fill this function in
    return pa2page(PTE_ADDR(*pte));
}
  1. page_lookup()
void page_remove(pde_t* pgdir, void* va) {
    pte_t* pte;
    struct PageInfo* page = page_lookup(pgdir, va, &pte);
    if (page == NULL) {
        return;
    }
    page_decref(page);
    tlb_invalidate(pgdir, va);
    *pte = 0;
    // Fill this function in
}
  1. page_insert()
int page_insert(pde_t* pgdir, struct PageInfo* pp, void* va, int perm) {
    pte_t* pte = pgdir_walk(pgdir, va, 1);
    if (pte == NULL) {
        return -E_NO_MEM; // 沒有空間了
    }
    // 一定要在page_remove之前增加引用,否則可能被加入page_free_list
    pp->pp_ref++;
    if (*pte & PTE_P) { // 如果該頁表項已經存在
        // 否則移除另一個va的映射
        page_remove(pgdir, va);
    }
    *pte = page2pa(pp) | perm | PTE_P;
    // Fill this function in
    return 0;
}

運行make qemu,會輸出:

check_page() succeeded!

Part 3: Kernel Address Space

Exercise 5

填寫mem_init()中調用check_page()后的代碼

boot_map_region(kern_pgdir, UPAGES, npages * sizeof(struct PageInfo), PADDR(pages), PTE_U);
boot_map_region(kern_pgdir, KSTACKTOP - KSTKSIZE, KSTKSIZE, PADDR(bootstack), PTE_W);
boot_map_region(kern_pgdir, KERNBASE, 0xffffffff - KERNBASE, 0, PTE_W);

最后make qemu,會輸出:

check_kern_pgdir() succeeded!
check_page_free_list() succeeded!
check_page_installed_pgdir() succeeded!

Question

  1. What entries (rows) in the page directory have been filled in at this point? What addresses do they map and where do they point? In other words, fill out this table as much as possible:
Entry Base Virtual Address Points to (logically):
1023 0xffc00000 Page table for top 4MB of phys memory
... ... ...
960 0xf0000000 Page table for [0,4)MB of phys memory
959 0xefc00000 Kernel Stack and Invalid Memory
... ... ...
957 0xef400000 UVPT, User read-only virtual page table
956 0xef000000 UPAGES, Read-only copies of the Page structures
... ... ...
  1. We have placed the kernel and user environment in the same address space. Why will user programs not be able to read or write the kernel's memory? What specific mechanisms protect the kernel memory?

Page-Directory Entry 和 Page-Table Entry 的U/S位(User/supervisor),如果沒有將其置1,那么用戶將沒有訪問權限

  1. What is the maximum amount of physical memory that this operating system can support? Why?

2^32 bytes = 4 GB

  1. How much space overhead is there for managing memory, if we actually had the maximum amount of physical memory? How is this overhead broken down?

需要使用到 1 個 page directory 和 1024 個 page table,1024*1024 個 PageInfo 結構體。

  1. 這個題上個實驗解釋的很清楚了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM