System Calls for Environment Creation

实验 3 中,我们只创建了一个进程运行,并没有允许用户进程产生新的子进程,我们需要在 JOS 系统允许用户进程通过系统调用创建新的用户进程。

传统上的 Unix 提供 fork()系统调用创建进程,创建出来的子进程复制了父进程的整个地址空间,除了进程号不同,在父进程中 fork()返回子进程的进程号,在子进程中返回 0,以此来区分父子进程。我们所要讨论的进程创建与 Unix 的类似。

由于进程创建往往涉及到内核操作,如果用户态允许创建进程的话,往往涉及到一些特权指令,比如进程的状态设置和内存分配;还有就是容易产生恶意代码对系统进行破坏;, 因此在创建进程时,我们需要用户进程陷入到内核态执行。以下即是我们需要实现 kern/syscall.c 中的函数:

  • static envid_tsys_exofork(void)
  • static int sys_env_set_status(envid_t envid, int status)
  • static int sys_page_alloc(envid_t envid, void *va, int perm)
  • static int sys_page_map(envid_t srcenvid, void srcva,envid_t dstenvid, void dstva, int perm) static int sys_page_unmap(envid_t envid, void *va)

Exercise 7. Implement the system calls described above in kern/syscall.c. You will need to use various functions in kern/pmap.c and kern/env.c, particularly envid2env(). For now, whenever you call envid2env(), pass 1 in the checkperm parameter. Be sure you check for any invalid system call arguments, returning -E_INVAL in that case. Test your JOS kernel with user/dumbfork and make sure it works before proceeding.

Challenge! Add the additional system calls necessary to read all of the vital state of an existing environment as well as set it up. Then implement a user mode program that forks off a child environment, runs it for a while (e.g., a few iterations of sys_yield()), then takes a complete snapshot or checkpoint of the child environment, runs the child for a while longer, and finally restores the child environment to the state it was in at the checkpoint and continues it from there. Thus, you are effectively "replaying" the execution of the child environment from an intermediate state. Make the child environment perform some interaction with the user using sys_cgetc() or readline() so that the user can view and mutate its internal state, and verify that with your checkpoint/restart you can give the child environment a case of selective amnesia, making it "forget" everything that happened beyond a certain point.

void
sched_yield(void)
{
    struct Env *idle;
    struct Env *e, *runenv = NULL;
    int i, cur=0;
    if (curenv) cur=ENVX(curenv->env_id);
    else cur = 0;
    for (i = 0; i < NENV; ++i) {
        int j = (cur+i) % NENV;
        if (envs[j].env_status == ENV_RUNNABLE) {
            if (runenv==NULL || envs[j].pr < runenv->pr) 
                runenv = envs+j; 
        }
    }
    if (curenv && (curenv->env_status == ENV_RUNNING) && ((runenv==NULL) || (curenv->pr < runenv->pr))) {
        env_run(curenv);
    }
    if (runenv) {
        env_run(runenv);
    }
    sched_halt();
}

在kern/syscall.c中:

int sys_change_pr(int pr) {
    curenv->pr = pr;
    return 0;
}
envid_t
pfork(int pr)
{
    set_pgfault_handler(pgfault);
    envid_t envid;
    uint32_t addr;
    envid = sys_exofork();
    if (envid == 0) {
        thisenv = &envs[ENVX(sys_getenvid())];
        sys_change_pr(pr);
        return 0;
    }
    if (envid < 0)
        panic("sys_exofork: %e", envid);
    for (addr = 0; addr < USTACKTOP; addr += PGSIZE)
        if ((uvpd[PDX(addr)] & PTE_P) && (uvpt[PGNUM(addr)] & PTE_P)
            && (uvpt[PGNUM(addr)] & PTE_U)) {
            duppage(envid, PGNUM(addr));
        }
    if (sys_page_alloc(envid, (void *)(UXSTACKTOP-PGSIZE), PTE_U|PTE_W|PTE_P) < 0)
        panic("1");
    extern void _pgfault_upcall();
    sys_env_set_pgfault_upcall(envid, _pgfault_upcall);
    if (sys_env_set_status(envid, ENV_RUNNABLE) < 0)
        panic("sys_env_set_status");
    return envid;
    panic("fork not implemented");
}

修改hello.c以进行测试:

#include <inc/lib.h>
void
umain(int argc, char **argv)
{
    int i;
    for (i = 1; i <= 5; ++i) {
        int pid = pfork(i);
        if (pid == 0) {
            cprintf("child %x is now living!\n", i);
            int j;
            for (j = 0; j < 5; ++j) {
                cprintf("child %x is yielding!\n", i);
                sys_yield();
            }
            break;
        }
    }
}

results matching ""

    No results matching ""