partA
实现unix-like的fork(),允许用户进程去创建它自己的拷贝。
sys_exfork(void),传入父进程id使用env_alloc从空闲进程列表中分配一个Env,在env_alloc里面设置了env_pgdir
1.将父进程的env_tf复制给新的进程的env_tf;
2.设置进程状态图为ENV_NOT_RUNNABLE;
3,将存放返回值的寄存器值设置为0;
return 当前分配的进程id;
sys_exofork之后其实子进程的env_pgdir里面的内容这个时候都是空的,父进程拷贝自己的地址空间给子进程在fork()调用一些系统调用中实现。
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*e;
int ret=env_alloc(&e,curenv->env_id);
if(ret<0){
return ret;
}
e->env_tf=curenv->env_tf;
e->env_status=ENV_NOT_RUNNABLE;
e->env_tf.tf_regs.reg_eax=0;
return e->env_id;
}
sys_env_set_status(),设置指定环境的状态为ENV_RUNNABLE或ENV_NOT_RUNNABLE。
根据envid获取Env,设置Env中的status
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.
if(status!=ENV_NOT_RUNNABLE&&status!=ENV_RUNNABLE)
return -E_INVAL;
struct Env*e;
int ret=envid2env(envid,&e,1);
if(ret<0){
return ret;
}
e->env_status=status;
return 0;
}
sys_page_alloc,分配一页物理内存,并将其映射到给定进程地址空间中的给定虚拟地址。
注意va必须要是一个页对齐的虚拟地址。
// Allocate a page of memory and map it at 'va' with permission
// 'perm' in the address space of 'envid'.
// The page's contents are set to 0.
// If a page is already mapped at 'va', that page is unmapped as a
// side effect.
//
// perm -- PTE_U | PTE_P must be set, PTE_AVAIL | PTE_W may or may not be set,
// but no other bits may be set. See PTE_SYSCALL in inc/mmu.h.
//
// Return 0 on success, < 0 on error. Errors are:
// -E_BAD_ENV if environment envid doesn't currently exist,
// or the caller doesn't have permission to change envid.
// -E_INVAL if va >= UTOP, or va is not page-aligned.
// -E_INVAL if perm is inappropriate (see above).
// -E_NO_MEM if there's no memory to allocate the new page,
// or to allocate any necessary page tables.
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.
struct Env*e;
int ret=envid2env(envid,&e,1);
if(ret) return -E_BAD_ENV;
if((va>=(void*)UTOP)||(ROUNDDOWN(va,PGSIZE)!=va)) return -E_INVAL;
int flag=PTE_U|PTE_P;
if((perm&flag)!=flag) return -E_INVAL;
struct PageInfo*pg=page_alloc(1);
if(!pg) return -E_NO_MEM;
ret=page_insert(e->env_pgdir,pg,va,perm); //建立映射,就是修改目录和页表
if(ret){
page_free(pg);
return ret;
}
return 0;
}
sys_page_map(),复制一个页映射(not the contents of a page!!)from 一个进程的地址空间到另外一个,保留内存共享安排,以便新映射和旧映射都引用物理内存的同一页。
// Map the page of memory at 'srcva' in srcenvid's address space
// at 'dstva' in dstenvid's address space with permission 'perm'.
// Perm has the same restrictions as in sys_page_alloc, except
// that it also must not grant write access to a read-only
// page.
//
// Return 0 on success, < 0 on error. Errors are:
// -E_BAD_ENV if srcenvid and/or dstenvid doesn't currently exist,
// or the caller doesn't have permission to change one of them.
// -E_INVAL if srcva >= UTOP or srcva is not page-aligned,
// or dstva >= UTOP or dstva is not page-aligned.
// -E_INVAL is srcva is not mapped in srcenvid's address space.
// -E_INVAL if perm is inappropriate (see sys_page_alloc).
// -E_INVAL if (perm & PTE_W), but srcva is read-only in srcenvid's
// address space.
// -E_NO_MEM if there's no memory to allocate any necessary page tables.
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.
struct Env*se,*de;
int ret=envid2env(srcenvid,&se,1);
if(ret) return ret;
ret=envid2env(dstenvid,&de,1);
if(ret) return ret;
if((srcva>=(void*)UTOP)||(dstva>=(void*)UTOP)||
(ROUNDDOWN(srcva,PGSIZE)!=srcva)||(ROUNDDOWN(dstva,PGSIZE)!=dstva))
return -E_INVAL;
pte_t *pte;
struct PageInfo*pg=page_lookup(se->env_pgdir,srcva,&pte);
if(!pg) return -E_INVAL;
int flag=PTE_U|PTE_P;
if((perm&flag)!=flag) return -E_INVAL;
if(((*pte&PTE_W)==0)&&(perm&PTE_W)) return -E_INVAL;
ret=page_insert(de->env_pgdir,pg,dstva,perm);
if(ret<0)
return -E_NO_MEM;
return 0;
}