diff options
author | Robert Morris <[email protected]> | 2019-06-11 09:57:14 -0400 |
---|---|---|
committer | Robert Morris <[email protected]> | 2019-06-11 09:57:14 -0400 |
commit | 5753553213df8f9de851adb68377db43faecb91f (patch) | |
tree | 3b629ff54897fca414146677532cb459a2ed11ba /proc.c | |
parent | 91ba81110acd3163f7de3580b677eece0c57f5e7 (diff) | |
download | xv6-labs-5753553213df8f9de851adb68377db43faecb91f.tar.gz xv6-labs-5753553213df8f9de851adb68377db43faecb91f.tar.bz2 xv6-labs-5753553213df8f9de851adb68377db43faecb91f.zip |
separate source into kernel/ user/ mkfs/
Diffstat (limited to 'proc.c')
-rw-r--r-- | proc.c | 591 |
1 files changed, 0 insertions, 591 deletions
@@ -1,591 +0,0 @@ -#include "types.h" -#include "param.h" -#include "memlayout.h" -#include "riscv.h" -#include "proc.h" -#include "spinlock.h" -#include "defs.h" - -struct { - struct spinlock lock; - struct proc proc[NPROC]; -} ptable; - -struct cpu cpus[NCPU]; - -struct proc *initproc; - -int nextpid = 1; -extern void forkret(void); - -// for returning out of the kernel -extern void sysexit(void); - -static void wakeup1(void *chan); - -extern char trampout[]; // trampoline.S - -void -procinit(void) -{ - initlock(&ptable.lock, "ptable"); -} - -// Must be called with interrupts disabled, -// to prevent race with process being moved -// to a different CPU. -int -cpuid() -{ - int id = r_tp(); - return id; -} - -// Return this core's cpu struct. -// Interrupts must be disabled. -struct cpu* -mycpu(void) { - int id = cpuid(); - struct cpu *c = &cpus[id]; - return c; -} - -// Return the current struct proc *. -struct proc* -myproc(void) { - push_off(); - struct cpu *c = mycpu(); - struct proc *p = c->proc; - pop_off(); - return p; -} - -//PAGEBREAK: 32 -// Look in the process table for an UNUSED proc. -// If found, change state to EMBRYO and initialize -// state required to run in the kernel. -// Otherwise return 0. -static struct proc* -allocproc(void) -{ - struct proc *p; - - acquire(&ptable.lock); - - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++) - if(p->state == UNUSED) - goto found; - - release(&ptable.lock); - return 0; - -found: - p->state = EMBRYO; - p->pid = nextpid++; - - release(&ptable.lock); - - // Allocate a page for the kernel stack. - if((p->kstack = kalloc()) == 0){ - p->state = UNUSED; - return 0; - } - - // Allocate a trapframe page. - if((p->tf = (struct trapframe *)kalloc()) == 0){ - p->state = UNUSED; - return 0; - } - - // An empty user page table. - p->pagetable = proc_pagetable(p); - - // 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 = (uint64)p->kstack + PGSIZE; - - return p; -} - -// Create a page table for a given process, -// with no users pages, but with trampoline pages. -// Called both when creating a process, and -// by exec() when building tentative new memory image, -// which might fail. -pagetable_t -proc_pagetable(struct proc *p) -{ - pagetable_t pagetable; - - // An empty user page table. - pagetable = uvmcreate(); - - // map the trampoline code (for system call return) - // at the highest user virtual address. - // only the supervisor uses it, on the way - // to/from user space, so not PTE_U. - mappages(pagetable, TRAMPOLINE, PGSIZE, - (uint64)trampout, PTE_R | PTE_X); - - // map the trapframe, for trampoline.S. - mappages(pagetable, (TRAMPOLINE - PGSIZE), PGSIZE, - (uint64)(p->tf), PTE_R | PTE_W); - - return pagetable; -} - -// Free a process's page table, and free the -// physical memory the page table refers to. -// Called both when a process exits and from -// exec() if it fails. -void -proc_freepagetable(pagetable_t pagetable, uint64 sz) -{ - unmappages(pagetable, TRAMPOLINE, PGSIZE, 0); - unmappages(pagetable, TRAMPOLINE-PGSIZE, PGSIZE, 0); - uvmfree(pagetable, sz); -} - -// a user program that calls exec("/init") -// od -t xC initcode -uchar initcode[] = { - 0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x05, 0x02, 0x97, 0x05, 0x00, 0x00, 0x93, 0x85, 0x05, 0x02, - 0x9d, 0x48, 0x73, 0x00, 0x00, 0x00, 0x89, 0x48, 0x73, 0x00, 0x00, 0x00, 0xef, 0xf0, 0xbf, 0xff, - 0x2f, 0x69, 0x6e, 0x69, 0x74, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00 -}; - -//PAGEBREAK: 32 -// Set up first user process. -void -userinit(void) -{ - struct proc *p; - - p = allocproc(); - initproc = p; - - uvminit(p->pagetable, initcode, sizeof(initcode)); - p->sz = PGSIZE; - - // prepare for the very first kernel->user. - p->tf->epc = 0; - p->tf->sp = PGSIZE; - - safestrcpy(p->name, "initcode", sizeof(p->name)); - p->cwd = namei("/"); - - // this assignment to p->state lets other cores - // run this process. the acquire forces the above - // writes to be visible, and the lock is also needed - // because the assignment might not be atomic. - acquire(&ptable.lock); - - p->state = RUNNABLE; - - release(&ptable.lock); -} - -// Grow current process's memory by n bytes. -// Return 0 on success, -1 on failure. -int -growproc(int n) -{ - uint sz; - struct proc *p = myproc(); - - sz = p->sz; - if(n > 0){ - if((sz = uvmalloc(p->pagetable, sz, sz + n)) == 0) - return -1; - } else if(n < 0){ - if((sz = uvmdealloc(p->pagetable, sz, sz + n)) == 0) - return -1; - } - p->sz = sz; - return 0; -} - -// Create a new process, copying p as the parent. -// Sets up child kernel stack to return as if from system call. -int -fork(void) -{ - int i, pid; - struct proc *np; - struct proc *p = myproc(); - - // Allocate process. - if((np = allocproc()) == 0){ - return -1; - } - - // Copy user memory from parent to child. - uvmcopy(p->pagetable, np->pagetable, p->sz); - np->sz = p->sz; - - np->parent = p; - - // copy saved user registers. - *(np->tf) = *(p->tf); - - // Cause fork to return 0 in the child. - np->tf->a0 = 0; - - // increment reference counts on open file descriptors. - for(i = 0; i < NOFILE; i++) - if(p->ofile[i]) - np->ofile[i] = filedup(p->ofile[i]); - np->cwd = idup(p->cwd); - - safestrcpy(np->name, p->name, sizeof(p->name)); - - pid = np->pid; - - acquire(&ptable.lock); - - np->state = RUNNABLE; - - release(&ptable.lock); - - return pid; -} - -// Exit the current process. Does not return. -// An exited process remains in the zombie state -// until its parent calls wait(). -void -exit(void) -{ - struct proc *p = myproc(); - struct proc *pp; - int fd; - - if(p == initproc) - panic("init exiting"); - - // Close all open files. - for(fd = 0; fd < NOFILE; fd++){ - if(p->ofile[fd]){ - fileclose(p->ofile[fd]); - p->ofile[fd] = 0; - } - } - - begin_op(); - iput(p->cwd); - end_op(); - p->cwd = 0; - - acquire(&ptable.lock); - - // Parent might be sleeping in wait(). - wakeup1(p->parent); - - // Pass abandoned children to init. - for(pp = ptable.proc; pp < &ptable.proc[NPROC]; pp++){ - if(pp->parent == p){ - pp->parent = initproc; - if(pp->state == ZOMBIE) - wakeup1(initproc); - } - } - - // Jump into the scheduler, never to return. - p->state = ZOMBIE; - sched(); - panic("zombie exit"); -} - -// Wait for a child process to exit and return its pid. -// Return -1 if this process has no children. -int -wait(void) -{ - struct proc *np; - int havekids, pid; - struct proc *p = myproc(); - - acquire(&ptable.lock); - for(;;){ - // Scan through table looking for exited children. - havekids = 0; - for(np = ptable.proc; np < &ptable.proc[NPROC]; np++){ - if(np->parent != p) - continue; - havekids = 1; - if(np->state == ZOMBIE){ - // Found one. - pid = np->pid; - kfree(np->kstack); - np->kstack = 0; - kfree((void*)np->tf); - np->tf = 0; - proc_freepagetable(np->pagetable, np->sz); - np->pagetable = 0; - np->pid = 0; - np->parent = 0; - np->name[0] = 0; - np->killed = 0; - np->state = UNUSED; - release(&ptable.lock); - return pid; - } - } - - // No point waiting if we don't have any children. - if(!havekids || p->killed){ - release(&ptable.lock); - return -1; - } - - // Wait for children to exit. (See wakeup1 call in proc_exit.) - sleep(p, &ptable.lock); //DOC: wait-sleep - } -} - -//PAGEBREAK: 42 -// 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 proc *p; - struct cpu *c = mycpu(); - - c->proc = 0; - for(;;){ - // Enable interrupts on this processor. - intr_on(); - - // 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. - c->proc = p; - p->state = RUNNING; - - swtch(&c->scheduler, &p->context); - - // Process is done running for now. - // It should have changed its p->state before coming back. - c->proc = 0; - } - release(&ptable.lock); - } -} - -// Enter scheduler. Must hold only ptable.lock -// and have changed proc->state. Saves and restores -// intena because intena is a property of this -// kernel thread, not this CPU. It should -// be proc->intena and proc->noff, but that would -// break in the few places where a lock is held but -// there's no process. -void -sched(void) -{ - int intena; - struct proc *p = myproc(); - - if(!holding(&ptable.lock)) - panic("sched ptable.lock"); - if(mycpu()->noff != 1) - panic("sched locks"); - if(p->state == RUNNING) - panic("sched running"); - if(intr_get()) - panic("sched interruptible"); - - intena = mycpu()->intena; - swtch(&p->context, &mycpu()->scheduler); - mycpu()->intena = intena; -} - -// Give up the CPU for one scheduling round. -void -yield(void) -{ - acquire(&ptable.lock); //DOC: yieldlock - myproc()->state = RUNNABLE; - sched(); - release(&ptable.lock); -} - -// A fork child's very first scheduling by scheduler() -// will swtch to forkret. -void -forkret(void) -{ - static int first = 1; - - // Still holding ptable.lock from scheduler. - release(&ptable.lock); - - if (first) { - // Some initialization functions must be run in the context - // of a regular process (e.g., they call sleep), and thus cannot - // be run from main(). - first = 0; - iinit(ROOTDEV); - initlog(ROOTDEV); - } - - usertrapret(); -} - -// Atomically release lock and sleep on chan. -// Reacquires lock when awakened. -void -sleep(void *chan, struct spinlock *lk) -{ - struct proc *p = myproc(); - - if(p == 0) - panic("sleep"); - - if(lk == 0) - panic("sleep without lk"); - - // Must acquire ptable.lock in order to - // change p->state and then call sched. - // Once we hold ptable.lock, we can be - // guaranteed that we won't miss any wakeup - // (wakeup runs with ptable.lock locked), - // so it's okay to release lk. - if(lk != &ptable.lock){ //DOC: sleeplock0 - acquire(&ptable.lock); //DOC: sleeplock1 - release(lk); - } - // Go to sleep. - p->chan = chan; - p->state = SLEEPING; - - sched(); - - // Tidy up. - p->chan = 0; - - // Reacquire original lock. - if(lk != &ptable.lock){ //DOC: sleeplock2 - release(&ptable.lock); - acquire(lk); - } -} - -//PAGEBREAK! -// Wake up all processes sleeping on chan. -// The ptable lock must be held. -static void -wakeup1(void *chan) -{ - struct proc *p; - - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++) - if(p->state == SLEEPING && p->chan == chan) - p->state = RUNNABLE; -} - -// Wake up all processes sleeping on chan. -void -wakeup(void *chan) -{ - acquire(&ptable.lock); - wakeup1(chan); - release(&ptable.lock); -} - -// Kill the process with the given pid. -// Process won't exit until it returns -// to user space (see trap in trap.c). -int -kill(int pid) -{ - struct proc *p; - - acquire(&ptable.lock); - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ - if(p->pid == pid){ - p->killed = 1; - // Wake process from sleep if necessary. - if(p->state == SLEEPING) - p->state = RUNNABLE; - release(&ptable.lock); - return 0; - } - } - release(&ptable.lock); - return -1; -} - -// Copy to either a user address, or kernel address, -// depending on usr_dst. -// Returns 0 on success, -1 on error. -int -either_copyout(int user_dst, uint64 dst, void *src, uint64 len) -{ - struct proc *p = myproc(); - if(user_dst){ - return copyout(p->pagetable, dst, src, len); - } else { - memmove((char *)dst, src, len); - return 0; - } -} - -// Copy from either a user address, or kernel address, -// depending on usr_src. -// Returns 0 on success, -1 on error. -int -either_copyin(void *dst, int user_src, uint64 src, uint64 len) -{ - struct proc *p = myproc(); - if(user_src){ - return copyin(p->pagetable, dst, src, len); - } else { - memmove(dst, (char*)src, len); - return 0; - } -} - -// Print a process listing to console. For debugging. -// Runs when user types ^P on console. -// No lock to avoid wedging a stuck machine further. -void -procdump(void) -{ - static char *states[] = { - [UNUSED] "unused", - [EMBRYO] "embryo", - [SLEEPING] "sleep ", - [RUNNABLE] "runble", - [RUNNING] "run ", - [ZOMBIE] "zombie" - }; - struct proc *p; - char *state; - - for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ - if(p->state == UNUSED) - continue; - if(p->state >= 0 && p->state < NELEM(states) && states[p->state]) - state = states[p->state]; - else - state = "???"; - printf("%d %s %s", p->pid, state, p->name); - printf("\n"); - } -} - |