[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即可。     

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

This entry was posted in 操作系统 and tagged . Bookmark the permalink.

2 Responses to [MIT6.828]LAB2 Exercise & Question总结

  1. snail says:

    唔。。。发现2012版的把queue.h去掉了,没有了LIST_INIT这些宏了,好麻烦

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注