标签:
Exercise 1. In the file kern/pmap.c, you must implement code for the following functions (probably in the order given). boot_alloc() mem_init() (only up to the call to check_page_free_list(1)) page_init() page_alloc() page_free() check_page_free_list() and check_page_alloc() test your physical page allocator. You should boot JOS and see whether check_page_alloc() reports success. Fix your code so that it passes. You may find it helpful to add your own assert()s to verify that your assumptions are correct.
在lab1中,内存布局如下:
kernel是0xF0100000 - end 部分, 剩下4K大小是页目录表:
需要由函数boot_alloc填补。
这部分的地址都是线性地址,即line_addr: 0xF0100000 ==> phy_addr: 0x00100000
PADDR: 线性地址转物理地址, kva-KERNBASE
KADDR: 物理地址转线性地址,pva+KERNBASE
page2pa: 页表项转物理地址,全局变量pages表示页表项的起始地址, pp-pages表示第k个页, 则(pp-pages)<<PAGE_SHIFT则为页表项pp的物理地址。
1) boot_alloc
1 static void * 2 boot_alloc(uint32_t n) 3 { 4 static char *nextfree; // virtual address of next byte of free memory 5 char *result; 6 7 // Initialize nextfree if this is the first time. 8 // ‘end‘ is a magic symbol automatically generated by the linker, 9 // which points to the end of the kernel‘s bss segment: 10 // the first virtual address that the linker did *not* assign 11 // to any kernel code or global variables. 12 if (!nextfree) { 13 extern char end[]; 14 nextfree = (char *)ROUNDUP((char *) end, PGSIZE); 15 } 16 17 // Allocate a chunk large enough to hold ‘n‘ bytes, then update 18 // nextfree. Make sure nextfree is kept aligned 19 // to a multiple of PGSIZE. 20 // 21 // LAB 2: Your code here. 22 cprintf("boot_alloc memory at line_addr [%08x, %08x + %08x]\n", nextfree, nextfree, ROUNDUP(n, PGSIZE)); 23 result = nextfree; 24 nextfree += ROUNDUP(n, PGSIZE); 25 26 return result; 27 }
这里有个技巧,局部静态变量nextfree未初始化时默认为0,第一次会执行12-15行,再次调用则不会。
2) 完善mem_init
这里需要分配页表项,内存布局如下:
在mem_init中,已经通过kern_pgdir = (pde_t *) boot_alloc(PGSIZE);分配了页目录表,完善分配页表项PagesInfo:
1 ////////////////////////////////////////////////////////////////////// 2 // Allocate an array of npages ‘struct PageInfo‘s and store it in ‘pages‘. 3 // The kernel uses this array to keep track of physical pages: for 4 // each physical page, there is a corresponding struct PageInfo in this 5 // array. ‘npages‘ is the number of physical pages in memory. Use memset 6 // to initialize all fields of each struct PageInfo to 0. 7 // Your code goes here: 8 pages = (struct PageInfo* )boot_alloc(npages * sizeof(struct PageInfo)); 9 memset(pages, 0, npages * sizeof(struct PageInfo)); 10 cprintf("npages:%d, npages_basemem:%d, pages_addr:%08x\n", npages, npages_basemem, pages);
3) page_init
通过全局变量page_free_list,将所有的页表项PageInfo和4K大小的页一一映射。
内存分配 [PAGE0][PGSIZE, npages_basemem * PGSIZE)[IOPHYSMEM, EXTPHYSMEM)[EXTPHYSMEM, ...)
PAGE0留作BIOS和IDT等,PAGE1-npages_basemem可以分配,IOmem到EXTmem用于IO, 之后是EXTPHYSMEM,
EXTPHYSMEM的起始部分到nextfree会用作kernel、页目录、页表项等,应该从nextfree再开始分配。
1 void 2 page_init(void) 3 { 4 // The example code here marks all physical pages as free. 5 // However this is not truly the case. What memory is free? 6 // 1) Mark physical page 0 as in use. 7 // This way we preserve the real-mode IDT and BIOS structures 8 // in case we ever need them. (Currently we don‘t, but...) 9 // 2) The rest of base memory, [PGSIZE, npages_basemem * PGSIZE) 10 // is free. 11 // 3) Then comes the IO hole [IOPHYSMEM, EXTPHYSMEM), which must 12 // never be allocated. 13 // 4) Then extended memory [EXTPHYSMEM, ...). 14 // Some of it is in use, some is free. Where is the kernel 15 // in physical memory? Which pages are already in use for 16 // page tables and other data structures? 17 // 18 // Change the code to reflect this. 19 // NB: DO NOT actually touch the physical memory corresponding to 20 // free pages! 21 size_t i; 22 pages[0].pp_ref = 1; 23 pages[0].pp_link = NULL; 24 25 uint32_t nextfree = (uint32_t)boot_alloc(0); 26 cprintf("NPAGES: %d NPAGES_BASE_MEM: %d\n", npages, npages_basemem); 27 cprintf("NEXTFREE: %08x IOPHY: %08x EXT: %08x\n", nextfree - KERNBASE, IOPHYSMEM, EXTPHYSMEM); 28 for (i = 1; i < npages; i++) 29 { 30 if ((i >= (IOPHYSMEM / PGSIZE)) && (i < ((nextfree - KERNBASE)/ PGSIZE))) 31 { 32 pages[i].pp_ref = 1; 33 pages[i].pp_link = NULL; 34 } 35 else 36 { 37 pages[i].pp_ref = 0; 38 pages[i].pp_link = page_free_list; 39 page_free_list = &pages[i]; 40 } 41 } 42 }
4) page_alloc
page_alloc函数的实现. 就是把当前free list中的空闲页释放一个,然后更新page_free_list,让ta指向下一个空闲页即可
如果传入ALLOC_ZERO的flag,则用memset清零。
1 struct PageInfo * 2 page_alloc(int alloc_flags) 3 { 4 // Fill this function in 5 struct PageInfo* pginfo = NULL; 6 if (!page_free_list) 7 { 8 return NULL; 9 } 10 11 pginfo = page_free_list; 12 page_free_list = pginfo->pp_link; 13 if (alloc_flags & ALLOC_ZERO) 14 { 15 memset(page2kva(pginfo), 0, PGSIZE); 16 } 17 18 return pginfo; 19 }
5) page_free
对应的page_free就是把pp描述的page加入到free list当中去,使得pp成为最新的page_free_list.
1 void 2 page_free(struct PageInfo *pp) 3 { 4 // Fill this function in 5 // Hint: You may want to panic if pp->pp_ref is nonzero or 6 // pp->pp_link is not NULL. 7 8 assert(pp->pp_ref == 0 || pp->pp_link == NULL); 9 10 pp->pp_link = page_free_list; 11 page_free_list = pp; 12 }
在Linux下, 每个进程都有自己独立的地址空间, 32bit的系统下位4GB. 所以, 每个地址的长度都是四字节, 也正好是一个指针的大小. 在了解了Linux的分页机制之后, 可以看到一个Virtual address其实是由如下3个部分组成:
// A linear address ‘la‘ has a three-part structure as follows: // // +--------10------+-------10-------+---------12----------+ // | Page Directory | Page Table | Offset within Page | // | Index | Index | | // +----------------+----------------+---------------------+ // \--- PDX(la) --/ \--- PTX(la) --/ \---- PGOFF(la) ----/ // \---------- PGNUM(la) ----------/
页目录(Page directory)其实是一个长度为1024的整形数组, 里面的每个元素是指向每一个页表(Page table)的指针. 每个页表也是个长度为1024的整形数组, 里边的元素则是物理地址的值.
然一个虚拟地址的高10位是该地址对应的页目录索引, 用于获取页目录中指向该地址的页表的地址.
通过10~20位, 能够得到该地址在页表项的索引, 然后就能够得到该地址对应的物理地址, 最后, 虚拟地址的低12位加上物理地址的基地址. 就完成了由虚拟地址到物理地址的转换.
Mit os Lab 2. Memory Management
标签:
原文地址:http://www.cnblogs.com/ym65536/p/5618097.html