# qemu -kernel loads the kernel at 0x80000000 # and causes each CPU to jump there. # kernel.ld causes the following code to # be placed at 0x80000000. .section .text _entry: # set up a stack for C. # stack0 is declared in start.c, # with a 4096-byte stack per CPU. # sp = stack0 + (hartid * 4096) la sp, stack0 li a0, 1024*4 csrr a1, mhartid addi a1, a1, 1 mul a0, a0, a1 add sp, sp, a0 # jump to start() in start.c call start spin: j spin
// entry.S jumps here in machine mode on stack0. void start() { // ... 初始化工作 w_mepc((uint64)main); // ... 初始化工作 // switch to supervisor mode and jump to main(). asmvolatile("mret"); }
// Set up new context to start executing at forkret, // which returns to user space. memset(&p->context, 0, sizeof(p->context)); p->context.ra = (uint64)forkret; p->context.sp = p->kstack + PGSIZE;
// A fork child's very first scheduling by scheduler() // will swtch to forkret. void forkret(void) { staticint first = 1;
// Still holding p->lock from scheduler. release(&myproc()->lock);
if (first) { // File system initialization must be run in the context of a // regular process (e.g., because it calls sleep), and thus cannot // be run from main(). first = 0; fsinit(ROOTDEV); }
// // return to user space // void usertrapret(void) { structproc *p = myproc();
// we're about to switch the destination of traps from // kerneltrap() to usertrap(), so turn off interrupts until // we're back in user space, where usertrap() is correct. intr_off();
// send syscalls, interrupts, and exceptions to trampoline.S w_stvec(TRAMPOLINE + (uservec - trampoline));
// set up trapframe values that uservec will need when // the process next re-enters the kernel. p->trapframe->kernel_satp = r_satp(); // kernel page table p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack p->trapframe->kernel_trap = (uint64)usertrap; p->trapframe->kernel_hartid = r_tp(); // hartid for cpuid()
// set up the registers that trampoline.S's sret will use // to get to user space.
// set S Previous Privilege mode to User. unsignedlong x = r_sstatus(); x &= ~SSTATUS_SPP; // clear SPP to 0 for user mode x |= SSTATUS_SPIE; // enable interrupts in user mode w_sstatus(x);
// set S Exception Program Counter to the saved user pc. w_sepc(p->trapframe->epc);
// tell trampoline.S the user page table to switch to. uint64 satp = MAKE_SATP(p->pagetable);
// jump to trampoline.S at the top of memory, which // switches to the user page table, restores user registers, // and switches to user mode with sret. uint64 fn = TRAMPOLINE + (userret - trampoline); ((void (*)(uint64,uint64))fn)(TRAPFRAME, satp); }
c->proc = 0; for(;;){ // Avoid deadlock by ensuring that devices can interrupt. intr_on();
int nproc = 0; for(p = proc; p < &proc[NPROC]; p++) { acquire(&p->lock); if(p->state != UNUSED) { nproc++; } if(p->state == RUNNABLE) { // Switch to chosen process. It is the process's job // to release its lock and then reacquire it // before jumping back to us. p->state = RUNNING; c->proc = p; // 调度器线程调用swtch函数切换运行上下文,此时的ra就是下一行指令的地址,即c->proc = 0 swtch(&c->context, &p->context);
// Process is done running for now. // It should have changed its p->state before coming back. c->proc = 0; } release(&p->lock); } if(nproc <= 2) { // only init and sh exist intr_on(); asmvolatile("wfi"); } } }
swtch是一段汇编代码,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# Context switch # # void swtch(struct context *old, struct context *new); # # Save current registers in old. Load from new.
// Give up the CPU for one scheduling round. void yield(void) { structproc *p = myproc(); acquire(&p->lock); p->state = RUNNABLE; sched(); release(&p->lock); }