Lab2 0. 任务介绍
你将编写一个内存管理代码。主要分为两大部分。分别对物理内存和虚拟内存的管理。
对于物理内存,每次分配内存分配器会为你分配4096bytes。也称为一个页(在大部分操作系统中一个页的大小都是4B)你需要维护一个数据结构来记录哪个物理页是空闲的哪个物理页是已被占用的。以及有多少进程共享已分配的页。并且你需要编写程序来进行内存的分配和回收
对于虚拟内存,它将内核和用户软件使用的虚拟地址映射到物理内存中的地址。 x86硬件的内存管理单元(MMU)在指令使用内存时执行映射,查阅一组页面表。 您将根据我们提供的规范设置MMU的页面表。
1. Part1: Physical Page Management操作系统必须跟踪哪部分物理内存是被使用的以及哪部分物理内存是空闲的。你需要在切入到虚拟内存之前完成这一操作,因为当使用虚拟内存的时候我们需要页表来进行管理,而你需要为页表分配物理内存。
下面的图来自于[https://blog.csdn.net/qq_40871466/article/details/103922416]
你需要实现 kern/pmap.c的下列函数
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.
1.1 实现boot_alloc在lab1我们知道了pc的启动过程。这里是在内核执行的init.c中先调用了mem_init。
根据我的调试输出(printf)我发现.bss的地址是 0xf01156a0这个地址在虚拟地址0xf0000000之上。我们知道内核的虚拟地址是在0xf0000000为起点的。随后是内核的代码段+数据段然后就是.bss所以这里bss大于内核虚拟地址起始位置是合理的.
boot_alloc就是在.bss之上分配制定大小为n的区域。注意这里都是在虚拟内存地址下进行的操作
如果n为0则直接范围nextfree的地址。也就是把.bss向上取整(为了都符合一页一页的存储形式。分页管理)
如果不为0则为他分配内存。其实就是把nextfree的地址往后移动 (n * PGSIZE)。不过我们要返回这段地址的起始地址。就相当于一个page数组的起始地址
static void * boot_alloc(uint32_t n) { static char *nextfree; // virtual address of next byte of free memory char *result; if (!nextfree) { extern char end[]; cprintf("end is %08x\n",end); nextfree = ROUNDUP((char *) end, PGSIZE); } cprintf("nextfree is %08x\n",nextfree); // Allocate a chunk large enough to hold 'n' bytes, then update // nextfree. Make sure nextfree is kept aligned // to a multiple of PGSIZE. // // LAB 2: Your code here. if (n == 0) { return nextfree; } //allocate result = nextfree; nextfree = ROUNDUP((char *)(nextfree + n), PGSIZE); return result; } 1.2 实现mem_init在mem_init中我们会调用两次boot_alloc。第一次为了创建页目录。第二次则为了创建所有的物理页表。分别为它们分配内存然后memset成0。
// create initial page directory. kern_pgdir = (pde_t *) boot_alloc(PGSIZE); memset(kern_pgdir, 0, PGSIZE); ////////////////////////////////////////////////////////////////////// // Recursively insert PD in itself as a page table, to form // a virtual page table at virtual address UVPT. // (For now, you don't have understand the greater purpose of the // following line.) // Permissions: kernel R, user R kern_pgdir[PDX(UVPT)] = PADDR(kern_pgdir) | PTE_U | PTE_P; cprintf("Page nubmer %d\n",npages); ////////////////////////////////////////////////////////////////////// // Allocate an array of npages 'struct PageInfo's and store it in 'pages'. // The kernel uses this array to keep track of physical pages: for // each physical page, there is a corresponding struct PageInfo in this // array. 'npages' is the number of physical pages in memory. Use memset // to initialize all fields of each struct PageInfo to 0. // Your code goes here: // all 32768 number pages pages = (struct PageInfo *) boot_alloc(sizeof(struct PageInfo) * npages); memset(pages, 0, sizeof(struct PageInfo) * npages); 1.3 实现page_alloc这里给了我们示例代码。不过这里把所有的pages都初始化了成了0和可用。这显然是不合理的
根据下图和实验中给的提示。base_memory(也就是 1mb + extended_memoy)这一段 。的low memory是可以被分配成use的
但是注意最下面的一个page不可以(实验中有提到保存实模式的一些信息)
![](/Users/zhouxiaolun/Library/Application Support/typora-user-images/image-20210623224201539.png)
第二就是extended memory里会存有内核的信息。我们要找到内核的结束位置,然后给剩余部分进行初始化
这里就可以简单利用boot_alloc(0)。因为这个会返回内核的结束位置对应的虚拟地址。
但是我们要找的是这个虚拟地址定于的物理地址在哪个page中。也就是要找到它在pages数组中的标号。
这里实验给我们提供了一个宏定义page2kva即可获得它对应的物理地址。然后除页表大小就可以获得对应的标号