When the processor performs a call to the exception- or interrupt-handler procedure(P198):
If the handler procedure is going to be executed at a numerically lower privilege level, a stack switch occurs. When the stack switch occurs: 1. The segment selector and stack pointer for the stack to be used by the handler are obtained from the TSS for the currently executing task. On this new stack, the processor pushes the stack segment selector and stack pointer of the interrupted procedure. 2. The processor then saves the current state of the EFLAGS, CS, and EIP registers on the new stack (see Figures 6-4). 3. If an exception causes an error code to be saved, it is pushed on the new stack after the EIP value.
If the handler procedure is going to be executed at the same privilege level as the interrupted procedure:
The processor saves the current state of the EFLAGS, CS, and EIP registers on the current stack (see Figures 6-4).
If an exception causes an error code to be saved, it is pushed on the current stack after the EIP value.
也就是说只有跨 ring 的时候,CPU 才会从 TR 寄存器里找 TSS 切到对应的内核栈,否则就直接用当前的栈就行,所以可以允许嵌套的中断栈帧。
void trap(struct trapframe *tf) { // dispatch based on what type of trap occurred // 向前面的实验兼容,基本用不到 if (current == NULL) { trap_dispatch(tf); } else { // keep a trapframe chain in stack struct trapframe *otf = current->tf; current->tf = tf; bool in_kernel = trap_in_kernel(tf); trap_dispatch(tf); current->tf = otf; if (!in_kernel) { // 该进程需要退出的话就退出 if (current->flags & PF_EXITING) { do_exit(-E_KILLED); } // 需要调度的话就调度 if (current->need_resched) { schedule(); } } } }
ucore允许中断嵌套。如果是用户态,有可能会出现第一次中断从用户态蹦到内核态,第二次在内核态又引起了一次中断,第一次因为跨了 ring 被切换到内核栈里,第二次没有跨 ring 所以又在原来内核栈运行到的地方再压了一层中断帧,所以可以形成两个栈帧,trap_in_kernel 通过判断当前的被保存的CS段是否是 KERNEL_CS 来判断中断是否发生在内核。
其次这应该是一个递归结构,在第 n 层处理完中断后,把当前的中断设置为 n-1 层的中断,再判断一下是否需要调度,不需要的话就返回上面一层 trap 接着处理(这种判断只发生在用户态蹦到内核态的那次中断,因为只有那个时候CS段是用户的代码段,防止在内核的时候也被抢占,导致内核线程的竞争,内核态发生的中断直接蹦到上一层中断处理)。
/* $begin eval */ /* eval - Evaluate a command line */ voideval(char *cmdline) { char *argv[MAXARGS]; /* Argument list execve() */ char buf[MAXLINE]; /* Holds modified command line */ // 是否以 & 结尾 int bg; /* Should the job run in bg or fg? */ pid_t pid; /* Process id */
strcpy(buf, cmdline); bg = parseline(buf, argv);
if (!builtin_command(argv)) { if ((pid = Fork()) == 0) { /* Child runs user job */ if (execve(argv[0], argv, environ) < 0) { printf("%s: Command not found.\n", argv[0]); exit(0); } }
/* Parent waits for foreground job to terminate */ if (!bg) { int status; if (waitpid(pid, &status, 0) < 0) unix_error("waitfg: waitpid error"); } else printf("%d %s", pid, cmdline); } return; }
tatic int load_icode(unsignedchar *binary, size_t size) { 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 if ((mm = mm_create()) == NULL) goto bad_mm; //建个新的PDT, 并把内核上3G地址页目录表拷过去 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 structPage *page; //(3.1) get the file header of the bianry program (ELF format) structelfhdr *elf = (struct elfhdr *)binary; //(3.2) get the entry of the program section headers of the bianry program (ELF format) structproghdr *ph = (struct proghdr *)(binary + elf->e_phoff); //(3.3) This program is valid? 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 //ELF_PT_LOAD表示一个可加载的段,段的大小由 p_filesz 和 p_memsz 描述。 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) // vma 应该是一个段一个(一并记录该段的读写等权限) 并且页表权限一开始就与了PTE_U,让用户有权限看到自己的页表 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; //p_vaddr 段在内存中的虚拟地址 //查看这段在vma中是否存在,不存在新建一个vma并插到mm里 if ((ret = mm_map(mm, ph->p_va, ph->p_memsz, vm_flags, NULL)) != 0) goto bad_cleanup_mmap;
//下面开始向OS申请空间,复制ELF的内容到虚拟空间中,并填好对应的页表 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) end = ph->p_va + ph->p_filesz; //(3.6.1) copy TEXT/DATA section of bianry program while (start < end) { //根据la的地址,获取对应的一页内存,不存在申请一页并挂到页目录上 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; }
//(3.6.2) build BSS section of binary program end = ph->p_va + ph->p_memsz; if (start < la) { /* ph->p_memsz == ph->p_filesz */ if (start == end) { continue; } off = start + PGSIZE - la, size = PGSIZE - off; if (end < la) { size -= la - end; } memset(page2kva(page) + off, 0, size); start += size; assert((end < la && start == end) || (end >= la && start == la)); } 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; } memset(page2kva(page) + off, 0, size); start += size; } }
//(5) set current process's mm, sr3, and set CR3 reg = physical addr of Page Directory mm_count_inc(mm); current->mm = mm; current->cr3 = PADDR(mm->pgdir); lcr3(PADDR(mm->pgdir));
// do_exit - called by sys_exit // 1. call exit_mmap & put_pgdir & mm_destroy to free the almost all memory space of process // 2. set process' state as PROC_ZOMBIE, then call wakeup_proc(parent) to ask parent reclaim itself. // 3. call scheduler to switch to other process intdo_exit(int error_code) { if (current == idleproc) { panic("idleproc exit.\n"); } if (current == initproc) { panic("initproc exit.\n"); }
structmm_struct *mm = current->mm; // 释放当前进程 mm if (mm != NULL) { lcr3(boot_cr3); if (mm_count_dec(mm) == 0) { exit_mmap(mm); put_pgdir(mm); mm_destroy(mm); } current->mm = NULL; }