[MIT6.828]LAB2 Exercise & Question总结

E1:In the file kern/pmap.c, you must implement code for the following functions.
    boot_alloc()
    page_init()
    page_alloc()
    page_free()


static void *boot_alloc(uint32_t n, uint32_t align) { extern char end[]; void *v; if (boot_freemem == 0) boot_freemem = end; //align boot_freemem = ROUNDUP(boot_freemem, align); v = boot_freemem; boot_freemem += n; return v; } void page_init(void) { extern char end[]; int i=0; LIST_INIT(&page_free_list); memset(pages, 0, npage*sizeof(pages[0])); //page 0 pages[0].pp_ref = 1; //rest basemem for (i=1 ; i < (basemem>>PGSHIFT); ++i) LIST_INSERT_HEAD(&page_free_list, &pages[i], pp_link); //page io for (i; i < (EXTPHYSMEM>>PGSHIFT); ++i) pages[i].pp_ref = 1; //externed mem //kernel code1 for (i; i < (ROUNDUP(PADDR(bootstack), PGSIZE)>>PGSHIFT); ++i) pages[i].pp_ref = 1; //kernel stack for (i; i < (KSTKSIZE>>PGSHIFT); ++i) pages[i].pp_ref = 2; //kernel code2 & static data for (i; i < (ROUNDUP(PADDR(end), PGSIZE)>>PGSHIFT); ++i) pages[i].pp_ref = 1; //Page directory pages[i++].pp_ref = 3; //Page list for(i; i< (ROUNDUP(PADDR(pages+npage), PGSIZE)>>PGSHIFT); ++i) pages[i].pp_ref = 2; //other boot alloc for(i; i< (ROUNDUP(PADDR(boot_freemem), PGSIZE)>>PGSHIFT); ++i) pages[i].pp_ref =1; //other for(i; i< npage; ++i) LIST_INSERT_HEAD(&page_free_list, &pages[i], pp_link); } int page_alloc(struct Page **pp_store) { *pp_store = LIST_FIRST(&page_free_list); //find a free page if (*pp_store == NULL) return -E_NO_MEM; //no free LIST_REMOVE(*pp_store, pp_link); // remove from list page_initpp(*pp_store); //clear return 0; } void page_free(struct Page *pp) { assert(pp->pp_ref ==0 ); // reference == 0 then free LIST_INSERT_HEAD(&page_free_list, pp, pp_link); }

最后在i386_vm_init()中把painc()那句删掉,然后指定地方加入
pages = boot_alloc(npage*sizeof(struct Page), PGSIZE);

在page_init中,那些被占用页的引用次数是从后面的代码中得到的,但因为这些页不会被释放的,所以写成什么都无所谓,但是写成正确的还是有助于系统完整性。

 

E2:看intel手册,明白分段保护的机制。

 

E3:实验看虚拟地址和对应的物理地址是否是相同的数据,略过。

 

E4:In the file kern/pmap.c, you must implement code for the following functions.
        pgdir_walk()
        boot_map_segment()
        page_lookup()
        page_remove()
        page_insert()

//需要注意物理地址和虚拟地址之间的转化! pte_t *pgdir_walk(pde_t *pgdir, const void *va, int create) { pte_t *pptab; struct Page *pp; pgdir = pgdir + PDX(va);// locate the pde if (! (*pgdir & PTE_P) ) {// not present if ( !create ) return NULL; //create page table; if ( page_alloc(&pp) == -E_NO_MEM ) return NULL;//no free page pptab = (pte_t *)page2kva(pp); // VIRTUAL ADDR!!! memset(pptab, 0, PGSIZE); // clear *pgdir = PADDR(pptab)|PTE_USER;// pde & priority pp->pp_ref++; //reference ++ } else pptab =(pte_t *) KADDR(PTE_ADDR(*pgdir)); // VIRTUAL ADDR!!! return pptab+PTX(va); } //注意处理 页被重新映射到同一块地址的情况,权限位要变,TLB要失效,引用计数不变。 int page_insert(pde_t *pgdir, struct Page *pp, void *va, int perm) { pte_t *ppte = pgdir_walk(pgdir, va, 1); if (ppte == NULL) //cann't alloc pagetable return -E_NO_MEM; if( ISPTE_P(*ppte)) {//present if (page2pa(pp) != PTE_ADDR(*ppte))//not equal, remove page_remove(pgdir, va); else tlb_invalidate(pgdir, va),pp->pp_ref --; } pp->pp_ref ++; *ppte = page2pa(pp)|perm|PTE_P; return 0; } //这里的映射仅仅是静态映射,不用给把物理地址对应的页面实际分配出来,并且注意地址溢出的情况。 static void boot_map_segment(pde_t *pgdir, uintptr_t la, size_t size, physaddr_t pa, int perm) { pte_t *ppte; la = ROUNDDOWN(la, PGSIZE); uintptr_t lla = la +size; while ( la!=lla )//ATTENTION overflow!!! { ppte = pgdir_walk(pgdir, (void *)(la), 1); assert( ppte!=NULL || ISPTE_P(*ppte));//null or remap *ppte = PTE_ADDR(pa)|perm|PTE_P; la += PGSIZE; pa += PGSIZE; } } struct Page *page_lookup(pde_t *pgdir, void *va, pte_t **pte_store) { pte_t *ppte = pgdir_walk(pgdir, va, 0); if (ppte == NULL)//page table not present return NULL; if (pte_store != NULL) *pte_store = ppte; return pa2page(*ppte); } void page_remove(pde_t *pgdir, void *va) { pte_t *ppte; struct Page *ppage; ppage = page_lookup(pgdir, va, &ppte); if (ppage != NULL && ISPTE_P(*ppte))//present { page_decref(ppage); *ppte = (pte_t)0; tlb_invalidate(pgdir, va); } }

 

Exercise 5.  Fill in the missing code in i386_vm_init() after the call to page_check().

在指定位置添加如下代码即可完成物理页结构,内核栈,全部256MB内存的映射:

boot_map_segment(pgdir, UPAGES, ROUNDUP(npage*sizeof(struct Page), PGSIZE), PADDR(pages), PTE_U|PTE_P); boot_map_segment(pgdir, KSTACK, ROUNDUP(KSTKSIZE,PGSIZE), PADDR(bootstack), PTE_W|PTE_P); boot_map_segment(pgdir, KERNBASE, ROUNDUP(~KERNBASE, PGSIZE), 0, PTE_W|PTE_P);

 

Q1:Assuming that the following JOS kernel code is correct, what type should variable x have, uintptr_t or physaddr_t?
    mystery_t x;
    char* value = return_a_pointer();
    *value = 10;
    x = (mystery_t) value;
value是可以直接操作虚拟内存的char * ,x是从value强制转化而来,没有经过虚拟内存转物理内存的步骤,所以mystery_t应该也是虚拟内存的一种,故x是uintptr_t类型。

Q2: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 0xffc0000 page table for top 4MB of phys memory 1022 0xff80000 page table for 248MB--(252MB-1) phys mem . . page table for ... phys mem 960 0xf000000(KERNBASE) page table for kernel code & static data 0--(4MB-1) phys mem 959 0xefc0000(VPT) page directory self(kernel RW) 958 0xef80000(ULIM) page table for kernel stack 957 0xef40000(UVPT) same as 959 (user kernel R) 956 0xef00000(UPAGES) page table for struct Pages[] . . NULL 1 0x00400000 NULL 0 0x00000000 same as 960 (then turn to NULL)

Q3:After check_boot_pgdir(), i386_vm_init() maps the first four MB of virtual address space to the first four MB of physical memory, then deletes this mapping at the end of the function. Why is this mapping necessary? What would happen if it were omitted? Does this actually limit our kernel to be 4MB? What must be true if our kernel were larger than 4MB?
在开启了分页,但是虚拟内存的段基址仍然是0xf0000000的时候,内核的虚拟地址转化位线性地址后是定位到pgdir[0]页目录项0,所以pgdir[0]必须保存有内核物理页表地址。pgdir[0]=pgdir[PDX(KERNBASE)];这句代码就是做的这件事。直到后面加载完新的GDT时候,内核虚拟地址等同于线性地址,线性地址直接映射到pgdir[PDX(KERNBASE)]上,pgdir[0]就可以清空了。

Q4:(From Lecture 4) 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?
因为我们的页表和页目录有PTE_U位,可以来控制用户是否可以访问某页。

 

Q5:What is the maximum amount of physical memory that this operating system can support? Why?
从目前的代码来看,一共映射了0--256M-1的物理内存地址空间,所以最多是访问256MB的内存。

 

Q6: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?
[0, 4K) 保留给实模式的IDT和BIOS [640K,1M) 用于IO映射 [., end) 用户内核代码和数据存储 [., ROUNDUP(.,PAGE)) 用于内存对齐 [., .+PGSIZE) 用于保存页目录 [., .+12*phymem/4K) 用于保存物理页信息 4K*2 用于保存内核栈页表和pages映射页表。 phymem/4M 用于保存其他全部页表

目前来看就是这么多,如果要计算物理内存位256MB的极限情况,把256MB带入phymem即可。     

总结:大胆猜想,小心求证,高屋建瓴,细致入微。

你可能感兴趣的:(list,struct,table,null,insert,reference)