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. 这个题上个实验解释的很清楚了

猜你喜欢

转载自www.cnblogs.com/zsmumu/p/12585245.html