码迷,mamicode.com
首页 > 其他好文 > 详细

Mit os Lab 2. Memory Management

时间:2016-06-26 18:14:10      阅读:527      评论:0      收藏:0      [点我收藏+]

标签:

Part 1: Physical Page Management

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 }

 

Part 2: Virtual Memory

在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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!