lab4

这一个lab主要学习进程管理和进程通讯,come on,好好学习

PART A: 多处理器支持

Exercise 1:
void *
mmio_map_region(physaddr_t pa, size_t size)
{
	// Where to start the next region.  Initially, this is the
	// beginning of the MMIO region.  Because this is static, its
	// value will be preserved between calls to mmio_map_region
	// (just like nextfree in boot_alloc).
	static uintptr_t base = MMIOBASE;

	// Reserve size bytes of virtual memory starting at base and
	// map physical pages [pa,pa+size) to virtual addresses
	// [base,base+size).  Since this is device memory and not
	// regular DRAM, you'll have to tell the CPU that it isn't
	// safe to cache access to this memory.  Luckily, the page
	// tables provide bits for this purpose; simply create the
	// mapping with PTE_PCD|PTE_PWT (cache-disable and
	// write-through) in addition to PTE_W.  (If you're interested
	// in more details on this, see section 10.5 of IA32 volume
	// 3A.)
	//
	// Be sure to round size up to a multiple of PGSIZE and to
	// handle if this reservation would overflow MMIOLIM (it's
	// okay to simply panic if this happens).
	//
	// Hint: The staff solution uses boot_map_region.
	//
	// Your code here:
	uintptr_t nextBase=base+ROUNDUP(size,PGSIZE);
	uintptr_t i;
	if(nextBase>=MMIOLIM) panic(" mmio_map_region: mmio is out of limit\n");
	boot_map_region(kern_pgdir,base,ROUNDUP(size,PGSIZE),pa,PTE_PCD|PTE_PWT|PTE_W);
	i=base;
	base=nextBase;
	return (void *)i;
	//panic("mmio_map_region not implemented");
}


Exercise 2:
void
page_init(void)
{
	
	size_t i;
	uint32_t j=((uint32_t)boot_alloc(0)-KERNBASE)/PGSIZE;
	uint32_t j2=IOPHYSMEM/PGSIZE;
	uint32_t j3=MPENTRY_PADDR/PGSIZE;
	for (i = npages-1; i>=1; i--) {
		//the physical memory pages that has been used by pages and kernel 
		
		//cprintf("rrrrrrrrrrrrrrrrr");
		if(!((i>=j2&&i<j)||i==0||i==j3)){
			pages[i].pp_ref = 0;
			pages[i].pp_link = page_free_list;
			page_free_list = &pages[i];
		}
	}
}


Question 1:
MPBOOTPHYS(start32)是取得start32的物理地址
为什么ljmpl $(PROT_MODE_CSEG),$(MPBOOTPHYS(start32),中需要取得start32的物理地址?
在boot/boot.s中,0-4M线性地址空间被映射到0-4M物理内存,start的线性地址与实际的物理地址一样,使用相对地址跳转后,还是跳转到protcseg, 所以不需要求物理地址;
而在这里,mpentry.s从别的地方拷贝过来的,其中start32是相对于mpentry_start的地址,这个地址与启用用保护模式后start32所在的物理地址不同,所以跳转不到start32指令处。
记住ljmpl后,就是跳转到cs+EIP的地址去,这里的内容就是cs+EIP转化为物理地址后,这个物理内存的内容。
深入理解跳转的含义!!!
Exercise 3:
static void
mem_init_mp(void)
{

	// LAB 4: Your code here:
	uint32_t i,stacki_beginV,stacki_beginPh;
	for(i=0;i<NCPU;i++){
	 stacki_beginV=(uint32_t)(KSTACKTOP-i*(KSTKSIZE + KSTKGAP)-KSTKSIZE);
	 //stacki_beginPh=(uint32_t)(bootstacktop-i*(KSTKSIZE + KSTKGAP)-KSTKSIZE);
	 boot_map_region(kern_pgdir,stacki_beginV,KSTKSIZE,PADDR(percpu_kstacks[i]),PTE_W|PTE_P);
	}

}


Exercise 4:

void
trap_init_percpu(void)
{
// LAB 4: Your code here:

	// Setup a TSS so that we get the right stack
	// when we trap to the kernel.
	//ts.ts_esp0 = KSTACKTOP;
	//ts.ts_ss0 = GD_KD;
	uint8_t i=thiscpu->cpu_id;
	thiscpu->cpu_ts.ts_esp0=KSTACKTOP-i*(KSTKSIZE+KSTKGAP);
	thiscpu->cpu_ts.ts_ss0=GD_KD;
	// Initialize the TSS slot of the gdt.
	//gdt[GD_TSS0 >> 3] = SEG16(STS_T32A, (uint32_t) (&ts),
	//				sizeof(struct Taskstate), 0);
	//gdt[GD_TSS0 >> 3].sd_s = 0;
	gdt[(GD_TSS0>>3)+i]=SEG16(STS_T32A,(uint32_t)(&thiscpu->cpu_ts),sizeof(struct Taskstate),0);
	gdt[(GD_TSS0>>3)+i].sd_s = 0;
	

	// Load the TSS selector (like other segment selectors, the
	// bottom three bits are special; we leave them 0)
	ltr(GD_TSS0+(i<<3));

	// Load the IDT
	lidt(&idt_pd);
}

Exercise 5: 很简单
Question 2: 为什么呢?user->kernel,使用kernel栈? 使用完后,下一次再进入kernel,栈中数据清空了!
                   考虑极端情况,如果某个cpu A在使用kernel, cpu B用户程序运行时产生中断,这时cpu B直接将数据保存到内核堆栈,而这时还没有申请big kernel lock呢!!!

Challenge 1:这个很难呀,很难! 

Exercise 6:这个花了我好长的时间,kernel总是崩掉,后来经过仔细调试,发现有两点影响这个问题:1. 以前lab所编写的函数env_setup_vm没有将这次试验中用到的MMIOBASE-MMIOLIM之间的内用拷贝到enviroment的页目录中去。2.就是trap_init_percpu中thiscpu->cpu_ts.ts_esp0的赋值,我一开始给出的是percpu_kstacks[i],不知为什么使用这个总是出现问题,必须使用KSTACKTOP-i*(KSTKSIZE+KSTKGAP), 可能有别的地方用到了这个地址了

static int
env_setup_vm(struct Env *e)
{
	int i;
	struct PageInfo *p = NULL;

	// Allocate a page for the page directory
	if (!(p = page_alloc(ALLOC_ZERO)))
		return -E_NO_MEM;

	// Now, set e->env_pgdir and initialize the page directory.
	//
	// Hint:
	//    - The VA space of all envs is identical above UTOP
	//	(except at UVPT, which we've set below).
	//	See inc/memlayout.h for permissions and layout.
	//	Can you use kern_pgdir as a template?  Hint: Yes.
	//	(Make sure you got the permissions right in Lab 2.)
	//    - The initial VA below UTOP is empty.
	//    - You do not need to make any more calls to page_alloc.
	//    - Note: In general, pp_ref is not maintained for
	//	physical pages mapped only above UTOP, but env_pgdir
	//	is an exception -- you need to increment env_pgdir's
	//	pp_ref for env_free to work correctly.
	//    - The functions in kern/pmap.h are handy.

	// LAB 3: Your code here.
	e->env_pgdir = (pde_t *)page2kva(p);
    memmove(e->env_pgdir, kern_pgdir, PGSIZE);
    memset(e->env_pgdir, 0, PDX(UTOP)*sizeof(pde_t));
    p->pp_ref ++;
	
	// UVPT maps the env's own page table read-only.
	// Permissions: kernel R, user R
	e->env_pgdir[PDX(UVPT)] = PADDR(e->env_pgdir) | PTE_P | PTE_U;

	return 0;
}
void
sched_yield(void)
{
	struct Env *idle;

	// Implement simple round-robin scheduling.
	//
	// Search through 'envs' for an ENV_RUNNABLE environment in
	// circular fashion starting just after the env this CPU was
	// last running.  Switch to the first such environment found.
	//
	// If no envs are runnable, but the environment previously
	// running on this CPU is still ENV_RUNNING, it's okay to
	// choose that environment.
	//
	// Never choose an environment that's currently running on
	// another CPU (env_status == ENV_RUNNING). If there are
	// no runnable environments, simply drop through to the code
	// below to halt the cpu.

	// LAB 4: Your code here.
	
    int i;
	if(thiscpu->cpu_env != NULL) {
		int cur_env_id = thiscpu->cpu_env->env_id;
		cur_env_id = ENVX(cur_env_id);
		for(i = (cur_env_id+1)%NENV; i != cur_env_id; i = (i+1)%NENV) {
			if(envs[i].env_status == ENV_RUNNABLE)
				break;
		}
		if(i != cur_env_id)
		env_run(&envs[i]);
		if(thiscpu->cpu_env->env_status == ENV_RUNNING)
		env_run(thiscpu->cpu_env);
     }else{
        for(i = 0; i<NENV; i++){
			if(envs[i].env_status == ENV_RUNNABLE)
				break;
		}
		if(i!=NENV) env_run(&envs[i]);
     }
	// sched_halt never returns
	sched_halt();
}

Question 3: 所有envrioment中的kernel线性地址部分都一样,和没有进入environment前的kernel一样. 所以按理说exericse6中使用percpu_kstacks[i]也正确,但是不知为什么总是报中断问题!!
Question 4: 使用sys_yield(),从用户模式进入kernel模式,trap.c中就保存在envs中了
Challenge 2: 添加一个系统调用,设置priority, 这样可以设置每个env的优先级
Challenge 3: env添加一些保存的项目
Exercise 7:
static envid_t
sys_exofork(void)
{
	// Create the new environment with env_alloc(), from kern/env.c.
	// It should be left as env_alloc created it, except that
	// status is set to ENV_NOT_RUNNABLE, and the register set is copied
	// from the current environment -- but tweaked so sys_exofork
	// will appear to return 0.

	// LAB 4: Your code here.
	struct Env *newenv_store=NULL;
	int r=env_alloc(&newenv_store,curenv->env_id);
	if(r<0) {cprintf("env_alloc error: %e", r);return r;}
	newenv_store->env_tf=curenv->env_tf;
	//memmove((void *)&(newenv_store->env_tf),(const void *)&(curenv->env_tf), sizeof(struct Trapframe));
	newenv_store->env_status=ENV_NOT_RUNNABLE;
	newenv_store->env_tf.tf_regs.reg_eax=0;
	return newenv_store->env_id;
	//panic("sys_exofork not implemented");
}

static int
sys_env_set_status(envid_t envid, int status)
{
	// Hint: Use the 'envid2env' function from kern/env.c to translate an
	// envid to a struct Env.
	// You should set envid2env's third argument to 1, which will
	// check whether the current environment has permission to set
	// envid's status.

	// LAB 4: Your code here.
	struct Env *newenv_store=NULL;
	int i=envid2env(envid,&newenv_store,1);
	if(i<0) return -E_BAD_ENV;
	if((status==ENV_RUNNABLE)||(status==ENV_NOT_RUNNABLE)){
		newenv_store->env_status=status;
		return 0;
	}else{
	     cprintf("xxxxxxxxxxx   status %d\n",status);
	     return -E_INVAL;
	}

	
	//panic("sys_env_set_status not implemented");
}
static int
sys_page_alloc(envid_t envid, void *va, int perm)
{
	// Hint: This function is a wrapper around page_alloc() and
	//   page_insert() from kern/pmap.c.
	//   Most of the new code you write should be to check the
	//   parameters for correctness.
	//   If page_insert() fails, remember to free the page you
	//   allocated!

	// LAB 4: Your code here.
	if(((uint32_t)va>=UTOP)||((uint32_t)va%PGSIZE!=0))return -E_INVAL;
	if(!((perm&PTE_U)&&(perm&PTE_P)))return -E_INVAL;
	if(perm&(~PTE_SYSCALL))return -E_INVAL;
	struct Env *newenv_store=NULL;
	int i=envid2env(envid,&newenv_store,1);
	if(i<0) return -E_BAD_ENV;
	struct PageInfo *pageinfo=page_alloc(ALLOC_ZERO);
	if(pageinfo==NULL) return -E_NO_MEM;
	
	i=page_insert(newenv_store->env_pgdir,pageinfo,va,perm);
	if(i<0) {
		page_free(pageinfo);
		return -E_NO_MEM;
	}
	return 0;
	//panic("sys_page_alloc not implemented");
}

static int
sys_page_map(envid_t srcenvid, void *srcva,
	     envid_t dstenvid, void *dstva, int perm)
{
	// Hint: This function is a wrapper around page_lookup() and
	//   page_insert() from kern/pmap.c.
	//   Again, most of the new code you write should be to check the
	//   parameters for correctness.
	//   Use the third argument to page_lookup() to
	//   check the current permissions on the page.

	// LAB 4: Your code here.
	if(((uint32_t)srcva>=UTOP)||((uint32_t)srcva%PGSIZE!=0))return -E_INVAL;
	if(((uint32_t)dstva>=UTOP)||((uint32_t)dstva%PGSIZE!=0))return -E_INVAL;
	if(!((perm&PTE_U)&&(perm&PTE_P)))return -E_INVAL;
	if(perm&(~PTE_SYSCALL))return -E_INVAL;
	struct Env *newenv_store=NULL;
	int i=envid2env(srcenvid,&newenv_store,1);
	if(i<0) return -E_BAD_ENV;
	pte_t *ptet=NULL;
	struct PageInfo *pageinfo=page_lookup(newenv_store->env_pgdir,srcva,&ptet);
	if(pageinfo==NULL)return -E_INVAL;
	if(perm&PTE_W){
		if(!(*ptet&PTE_W))return -E_INVAL;
	}
	i=envid2env(dstenvid,&newenv_store,1);
	if(i<0) return -E_BAD_ENV;
	i=page_insert(newenv_store->env_pgdir,pageinfo,dstva,perm);
	if(i<0)return -E_NO_MEM;
	return 0;
	//panic("sys_page_map not implemented");
}

static int
sys_page_unmap(envid_t envid, void *va)
{
	// Hint: This function is a wrapper around page_remove().

	// LAB 4: Your code here.
	if(((uint32_t)va>=UTOP)||((uint32_t)va%PGSIZE!=0))return -E_INVAL;
	struct Env *newenv_store=NULL;
	int i=envid2env(envid,&newenv_store,1);
	if(i<0) return -E_BAD_ENV;
	pte_t *ptet=NULL;
	struct PageInfo *pageinfo=page_lookup(newenv_store->env_pgdir,va,&ptet);
	if(pageinfo==NULL)return 0;
	page_remove(newenv_store->env_pgdir,va);
	return 0;
	//panic("sys_page_unmap not implemented");
}

int32_t syscall中添加:
case SYS_exofork:
			r=sys_exofork();
			break;
		case SYS_page_alloc:
			r=sys_page_alloc((envid_t)a1,(void *) a2,(int)a3);
			break;
		case SYS_page_map:
			r=sys_page_map((envid_t)a1,(void*)a2,(envid_t)a3,(void*)a4,(int)a5);
			break;
		case SYS_page_unmap:
			r=sys_page_unmap((envid_t)a1,(void*)a2);
			break;
		case SYS_env_set_status:
			r=sys_env_set_status((envid_t)a1,(int)a2);
			break;

注意:sys_page_umap中我一开始未使用page_remove,而使用*ptet=0,哈哈,这样不行呀,导致系统崩溃!!!
Challenge 4: 这个和进程干冻差不多,哈哈,未做!

PART B: Copy-on-write fork

Exercise 9:
Exercise 10:
Exercise 11:
Challenge 4:怎么要处理user environment所有的异常呢?现代OS中都是这么搞的?为什么是用户程序来处理这些呢??
Exercise 12: 这几个exercises没认真做,看明白了ruizhe huang的代码, pfentry.s的代码注意,如何实现user unexception-> user normal模式和user->unexception? 将eip保存到空白中,esp指到这个空白处,然后pop %esp,然后ret(pop %eip),这样就跳转过去了,真的很tricky,很tricky
Challenge 5,6:未做 

PART C: Preemptive Multitasking and IPC

Exersice 13:很简单, 使用SEGGATE时,设置为中断门,而不是异常门。

Exersice 14:时钟中断为IRQ0,对应处理函数为routine_irq0

trap_dispatch中添加:

case (IRQ_OFFSET+IRQ_TIMER):
			lapic_eoi();
			sched_yield();
			return;


Exercise 15: 具体实现IPC,就是实现两个系统调用,send和recv,来实现进程通信。具体没做,看huangrui zhe的实验报告跑了跑
最后几个Challenge也未做
2013.7.29-2012.8.4
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值