/* load_icode - load the content of binary program(ELF format) as the new content of current process * @binary: the memory addr of the content of binary program * @size: the size of the content of binary program */ // 从内存中地址位置为binary的地方加载一个ELF格式的程序,加载到当前进程的内存空间中 // size是binary的大小 // 因为没有文件管理系统,所以只能从内存中加载 // 而应用程序随内核编译,被bootloader加载到内存中(参见实验指导书) staticintload_icode(unsignedchar *binary, size_t size) { // 需要保证当前进程的内存空间是空的 // 这在do_execve中已经保证了 if (current->mm != NULL) { panic("load_icode: current->mm must be empty.\n"); }
int ret = -E_NO_MEM; structmm_struct *mm; //(1) create a new mm for current process // 1. 创建一个新的mm_struct结构体,其实就是为新程序创建地址空间 if ((mm = mm_create()) == NULL) { goto bad_mm; }
//(2) create a new PDT, and mm->pgdir= kernel virtual addr of PDT // 2. 创建一个新的页目录表 if (setup_pgdir(mm) != 0) { goto bad_pgdir_cleanup_mm; }
//(3) copy TEXT/DATA section, build BSS parts in binary to memory space of process // 3. 加载程序,需要解析各种东西 structPage *page; //(3.1) get the file header of the bianry program (ELF format) // 3.1 获取ELF文件头 structelfhdr *elf = (struct elfhdr *)binary; //(3.2) get the entry of the program section headers of the bianry program (ELF format) // 3.2 获取ELF程序头表 structproghdr *ph = (struct proghdr *)(binary + elf->e_phoff); //(3.3) This program is valid? // 3.3 检查ELF文件头是否合法(魔数) if (elf->e_magic != ELF_MAGIC) { ret = -E_INVAL_ELF; goto bad_elf_cleanup_pgdir; }
uint32_t vm_flags, perm; structproghdr *ph_end = ph + elf->e_phnum; for (; ph < ph_end; ph++) { //(3.4) find every program section headers // 3.4 寻找每个程序段 if (ph->p_type != ELF_PT_LOAD) { continue; } if (ph->p_filesz > ph->p_memsz) { ret = -E_INVAL_ELF; goto bad_cleanup_mmap; } if (ph->p_filesz == 0) { continue; } //(3.5) call mm_map fun to setup the new vma ( ph->p_va, ph->p_memsz) // 3.5 设置新的虚拟内存区域 vm_flags = 0, perm = PTE_U; if (ph->p_flags & ELF_PF_X) vm_flags |= VM_EXEC; if (ph->p_flags & ELF_PF_W) vm_flags |= VM_WRITE; if (ph->p_flags & ELF_PF_R) vm_flags |= VM_READ; if (vm_flags & VM_WRITE) perm |= PTE_W; if ((ret = mm_map(mm, ph->p_va, ph->p_memsz, vm_flags, NULL)) != 0) { goto bad_cleanup_mmap; } unsignedchar *from = binary + ph->p_offset; size_t off, size; uintptr_t start = ph->p_va, end, la = ROUNDDOWN(start, PGSIZE);
ret = -E_NO_MEM;
//(3.6) alloc memory, and copy the contents of every program section (from, from+end) to process's memory (la, la+end) // 3.6 分配内存和复制上下文 end = ph->p_va + ph->p_filesz; //(3.6.1) copy TEXT/DATA section of bianry program // 3.6.1 复制TEXT/DATA段 while (start < end) { if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) { goto bad_cleanup_mmap; } off = start - la, size = PGSIZE - off, la += PGSIZE; if (end < la) { size -= la - end; } memcpy(page2kva(page) + off, from, size); start += size, from += size; }
//(5) set current process's mm, sr3, and set CR3 reg = physical addr of Page Directory // 设置当前线程的mm表、cr3寄存器和CR3寄存器 mm_count_inc(mm); current->mm = mm; current->cr3 = PADDR(mm->pgdir); lcr3(PADDR(mm->pgdir));
//(6) setup trapframe for user environment // 设置用户环境的trapframe structtrapframe *tf = current->tf; memset(tf, 0, sizeof(struct trapframe)); // 需要通过中断帧来实现中断返回 // 这样才能正确切换到目标线程的上下文里 // 此处修改的是current里的trapframe,即当前线程的tf /* LAB5:EXERCISE1 YOUR CODE * should set tf_cs,tf_ds,tf_es,tf_ss,tf_esp,tf_eip,tf_eflags * NOTICE: If we set trapframe correctly, then the user level process can return to USER MODE from kernel. So * tf_cs should be USER_CS segment (see memlayout.h) * tf_ds=tf_es=tf_ss should be USER_DS segment * tf_esp should be the top addr of user stack (USTACKTOP) * tf_eip should be the entry point of this binary program (elf->e_entry) * tf_eflags should be set to enable computer to produce Interrupt */ // 用户堆栈 tf->tf_cs = USER_CS; tf->tf_ds = tf->tf_es = tf->tf_ss = USER_DS; tf->tf_esp = USTACKTOP; // 设置下一条指令为程序入口 tf->tf_eip = elf->e_entry; tf->tf_eflags = FL_IF; ret = 0; out: return ret; bad_cleanup_mmap: exit_mmap(mm); bad_elf_cleanup_pgdir: put_pgdir(mm); bad_pgdir_cleanup_mm: mm_destroy(mm); bad_mm: goto out; }
// copy_mm - process "proc" duplicate OR share process "current"'s mm according clone_flags // - if clone_flags & CLONE_VM, then "share" ; else "duplicate" staticintcopy_mm(uint32_t clone_flags, struct proc_struct *proc) { structmm_struct *mm, *oldmm = current->mm;
/* current is a kernel thread */ // 内核线程不需要复制地址空间 if (oldmm == NULL) { return0; }
// 写时复制,目前没有,不会进入此if if (clone_flags & CLONE_VM) { mm = oldmm; goto good_mm; }
// 一般情况下,进程复制 int ret = -E_NO_MEM; // 创建子进程的mm if ((mm = mm_create()) == NULL) { goto bad_mm; } // 设置子进程的页目录 if (setup_pgdir(mm) != 0) { goto bad_pgdir_cleanup_mm; }
lock_mm(oldmm); { // 执行复制 ret = dup_mmap(mm, oldmm); } unlock_mm(oldmm);
/* copy_range - copy content of memory (start, end) of one process A to another process B * @to: the addr of process B's Page Directory * @from: the addr of process A's Page Directory * @share: flags to indicate to dup OR share. We just use dup method, so it didn't be used. * * CALL GRAPH: copy_mm-->dup_mmap-->copy_range */ intcopy_range(pde_t *to, pde_t *from, uintptr_t start, uintptr_t end, bool share) { assert(start % PGSIZE == 0 && end % PGSIZE == 0); assert(USER_ACCESS(start, end)); // copy content by page unit. do { // call get_pte to find process A's pte according to the addr start // 从父进程中的页表中找到对应的页表项 pte_t *ptep = get_pte(from, start, 0), *nptep; if (ptep == NULL) { start = ROUNDDOWN(start + PTSIZE, PTSIZE); continue; } // call get_pte to find process B's pte according to the addr start. If pte is NULL, just alloc a PT // 然后在子进程里找到对应的,找不到就新建一个,然后把父进程的复制过去 if (*ptep & PTE_P) { if ((nptep = get_pte(to, start, 1)) == NULL) { return -E_NO_MEM; } uint32_t perm = (*ptep & PTE_USER); // get page from ptep structPage *page = pte2page(*ptep); // alloc a page for process B structPage *npage = alloc_page(); assert(page != NULL); assert(npage != NULL); int ret = 0; /* LAB5:EXERCISE2 YOUR CODE * replicate content of page to npage, build the map of phy addr of nage with the linear addr start * * Some Useful MACROs and DEFINEs, you can use them in below implementation. * MACROs or Functions: * page2kva(struct Page *page): return the kernel vritual addr of memory which page managed (SEE pmm.h) * page_insert: build the map of phy addr of an Page with the linear addr la * memcpy: typical memory copy function * * (1) find src_kvaddr: the kernel virtual address of page * (2) find dst_kvaddr: the kernel virtual address of npage * (3) memory copy from src_kvaddr to dst_kvaddr, size is PGSIZE * (4) build the map of phy addr of nage with the linear addr start */ // 复制page的内容到npage,建立npage的物理地址与线性地址开始的映射。