Living@Greatwall

lab3:加载用户进程

该练习主要完成内核加载用户进程,包括用户进程环境的初始化,创建用户进程,加载用户进程等。 主要完成以下几个函数的实现: * env_init():初始化用户进程,配置每个段的优先级特权级;

  • env_setup_vm():给每个进程分配一个页目录,并初始化用户地址空间的内核部分;

  • region_alloc():给用户进程分配物理内存并将物理地址映射到进程的虚拟地址空间;

  • load_icode():解析用户进程elf文件,并加载到用户地址空间;

  • env_create():调用env_alloc和load_icode;

  • env_run():在用户模式下执行进程;

由于此时还没有文件系统,此时是将用户的进程elf文件嵌入到内核的image中,最后生成的 kernel.sym包含一些特殊的符号,这个符号实际上就是用户程序的elf文件,最后由内核加载 并执行。

每个用户进程都有一个相应的结构体:

struct Env {
    struct Trapframe env_tf;    // Saved registers
    struct Env *env_link;       // Next free Env
    envid_t env_id;         // Unique environment identifier
    envid_t env_parent_id;      // env_id of this env's parent
    enum EnvType env_type;      // Indicates special system environments
    unsigned env_status;        // Status of the environment
    uint32_t env_runs;      // Number of times environment has run

    // Address space
    pde_t *env_pgdir;       // Kernel virtual address of page dir
};

该练习的难点在于load_icode的实现(其它的实现就不解释了),该函数先解析用户的elf文件,解析时需要熟悉 elf文件的结构(前面文章已分析)。将每个段(.text, .data)解析后,同时需要拷贝用户进程的地址 空间。由于这个拷贝过程用到的虚拟地址是属于用户进程空间,所以这里要切换到用户进程的页目录, 待解析完后,再切换回内核的页目录。

static void
load_icode(struct Env *e, uint8_t *binary)
{

    // LAB 3: Your code here.
    struct Elf *elfh = (struct Elf *) binary;

    if (elfh->e_magic != ELF_MAGIC)
        goto bad;

    struct Proghdr *ph = (struct Proghdr *) (binary + elfh->e_phoff);
    struct Proghdr *eph = ph + elfh->e_phnum;

    lcr3(PADDR(e->env_pgdir));
    for (; ph < eph; ph++) {
        if (ph->p_type != ELF_PROG_LOAD) 
            continue;
        region_alloc(e, (void *)ph->p_va, ph->p_memsz);
        memset((void *)ph->p_va, 0, ph->p_memsz);
        memcpy((void *)ph->p_va, (void *)(binary + ph->p_offset), ph->p_filesz);
    }
    lcr3(PADDR(kern_pgdir));
    e->env_tf.tf_eip = elfh->e_entry; //set eip to elf->entry

    // Now map one page for the program's initial stack
    // at virtual address USTACKTOP - PGSIZE.

    // LAB 3: Your code here.
    region_alloc(e, (void *)(USTACKTOP - PGSIZE), PGSIZE);
    return;
bad:
    panic("load_icode: bad elf image\n");
}