// Per−CPU process scheduler. // Each CPU calls scheduler() after setting itself up. // Scheduler never returns. It loops, doing: // − choose a process to run // − swtch to start running that process // − eventually that process transfers control // via swtch back to the scheduler. void scheduler(void) { struct *p;
for(;;){ // Enable interrupts on this processor. sti();
// Loop over process table looking for process to run. acquire(&ptable.lock); for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ if(p−>state != RUNNABLE) continue;
// Switch to chosen process. It is the process’s job // to release ptable.lock and then reacquire it // before jumping back to us. proc = p; switchuvm(p); p−>state = RUNNING; swtch(&cpu−>scheduler, p−>context); switchkvm();
// Process is done running for now. // It should have changed its p−>state before coming back. proc = 0; } release(&ptable.lock);
} }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// Switch TSS and h/w page table to correspond to process p. void switchuvm(struct proc *p) { pushcli(); cpu−>gdt[SEG_TSS] = SEG16(STS_T32A, &cpu−>ts, sizeof(cpu−>ts)−1, 0); cpu−>gdt[SEG_TSS].s = 0; cpu−>ts.ss0 = SEG_KDATA << 3; cpu−>ts.esp0 = (uint)proc−>kstack + KSTACKSIZE; // setting IOPL=0 in eflags *and* iomb beyond the tss segment limit // forbids I/O instructions (e.g., inb and outb) from user space cpu−>ts.iomb = (ushort) 0xFFFF; ltr(SEG_TSS << 3); if(p−>pgdir == 0) panic("switchuvm: no pgdir"); lcr3(V2P(p−>pgdir)); // switch to process’s address space popcli(); }
// Per−process state struct { uint sz; // Size of process memory (bytes) pde_t* pgdir; // Page table char *kstack; // Bottom of kernel stack for this process enum procstate state; // Process state int pid; // Process ID struct *parent;// Parent process structtrapframe *tf;// Trap frame for current syscall, note: save register? structcontext *context;// swtch() here to run process, note: address of proc address void *chan; // If non−zero, sleeping on chan int killed; // If non−zero, have been killed structfile *ofile[NOFILE];// Open files, note: file table? structinode *cwd;// Current directory char name[16]; // Process name (debugging) };
// Process memory is laid out contiguously, low addresses first: // text // original data and bss // fixed−size stack // expandable heap
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// Saved registers for kernel context switches. // Don’t need to save all the segment registers (%cs, etc), // because they are constant across kernel contexts. // Don’t need to save %eax, %ecx, %edx, because the // x86 convention is that the caller has saved them. // Contexts are stored at the bottom of the stack they // describe; the stack pointer is the address of the context. // The layout of the context matches the layout of the stack in swtch.S // at the "Switch stacks" comment. Switch doesn’t save eip explicitly, // but it is on the stack and allocproc() manipulates it. structcontext { uint edi; uint esi; uint ebx; uint ebp; uint eip; };
近期评论