kernel/proc.c

690 lines
1#include "types.h"2#include "param.h"3#include "memlayout.h"4#include "riscv.h"5#include "spinlock.h"6#include "proc.h"7#include "defs.h"89struct cpu cpus[NCPU];1011struct proc proc[NPROC];1213struct proc *initproc;1415int nextpid = 1;16struct spinlock pid_lock;1718extern void forkret(void);19static void freeproc(struct proc *p);2021extern char trampoline[]; // trampoline.S2223// helps ensure that wakeups of wait()ing24// parents are not lost. helps obey the25// memory model when using p->parent.26// must be acquired before any p->lock.27struct spinlock wait_lock;2829// Allocate a page for each process's kernel stack.30// Map it high in memory, followed by an invalid31// guard page.32void33proc_mapstacks(pagetable_t kpgtbl)34{35  struct proc *p;36  37  for(p = proc; p < &proc[NPROC]; p++) {38    char *pa = kalloc();39    if(pa == 0)40      panic("kalloc");41    uint64 va = KSTACK((int) (p - proc));42    kvmmap(kpgtbl, va, (uint64)pa, PGSIZE, PTE_R | PTE_W);43  }44}4546// initialize the proc table.47void48procinit(void)49{50  struct proc *p;51  52  initlock(&pid_lock, "nextpid");53  initlock(&wait_lock, "wait_lock");54  for(p = proc; p < &proc[NPROC]; p++) {55      initlock(&p->lock, "proc");56      p->state = UNUSED;57      p->kstack = KSTACK((int) (p - proc));58  }59}6061// Must be called with interrupts disabled,62// to prevent race with process being moved63// to a different CPU.64int65cpuid()66{67  int id = r_tp();68  return id;69}7071// Return this CPU's cpu struct.72// Interrupts must be disabled.73struct cpu*74mycpu(void)75{76  int id = cpuid();77  struct cpu *c = &cpus[id];78  return c;79}8081// Return the current struct proc *, or zero if none.82struct proc*83myproc(void)84{85  push_off();86  struct cpu *c = mycpu();87  struct proc *p = c->proc;88  pop_off();89  return p;90}9192int93allocpid()94{95  int pid;96  97  acquire(&pid_lock);98  pid = nextpid;99  nextpid = nextpid + 1;100  release(&pid_lock);101102  return pid;103}104105// Look in the process table for an UNUSED proc.106// If found, initialize state required to run in the kernel,107// and return with p->lock held.108// If there are no free procs, or a memory allocation fails, return 0.109static struct proc*110allocproc(void)111{112  struct proc *p;113114  for(p = proc; p < &proc[NPROC]; p++) {115    acquire(&p->lock);116    if(p->state == UNUSED) {117      goto found;118    } else {119      release(&p->lock);120    }121  }122  return 0;123124found:125  p->pid = allocpid();126  p->state = USED;127128  // Allocate a trapframe page.129  if((p->trapframe = (struct trapframe *)kalloc()) == 0){130    freeproc(p);131    release(&p->lock);132    return 0;133  }134135  // An empty user page table.136  p->pagetable = proc_pagetable(p);137  if(p->pagetable == 0){138    freeproc(p);139    release(&p->lock);140    return 0;141  }142143  // Set up new context to start executing at forkret,144  // which returns to user space.145  memset(&p->context, 0, sizeof(p->context));146  p->context.ra = (uint64)forkret;147  p->context.sp = p->kstack + PGSIZE;148149  return p;150}151152// free a proc structure and the data hanging from it,153// including user pages.154// p->lock must be held.155static void156freeproc(struct proc *p)157{158  if(p->trapframe)159    kfree((void*)p->trapframe);160  p->trapframe = 0;161  if(p->pagetable)162    proc_freepagetable(p->pagetable, p->sz);163  p->pagetable = 0;164  p->sz = 0;165  p->pid = 0;166  p->parent = 0;167  p->name[0] = 0;168  p->chan = 0;169  p->killed = 0;170  p->xstate = 0;171  p->state = UNUSED;172}173174// Create a user page table for a given process, with no user memory,175// but with trampoline and trapframe pages.176pagetable_t177proc_pagetable(struct proc *p)178{179  pagetable_t pagetable;180181  // An empty page table.182  pagetable = uvmcreate();183  if(pagetable == 0)184    return 0;185186  // map the trampoline code (for system call return)187  // at the highest user virtual address.188  // only the supervisor uses it, on the way189  // to/from user space, so not PTE_U.190  if(mappages(pagetable, TRAMPOLINE, PGSIZE,191              (uint64)trampoline, PTE_R | PTE_X) < 0){192    uvmfree(pagetable, 0);193    return 0;194  }195196  // map the trapframe page just below the trampoline page, for197  // trampoline.S.198  if(mappages(pagetable, TRAPFRAME, PGSIZE,199              (uint64)(p->trapframe), PTE_R | PTE_W) < 0){200    uvmunmap(pagetable, TRAMPOLINE, 1, 0);201    uvmfree(pagetable, 0);202    return 0;203  }204205  return pagetable;206}207208// Free a process's page table, and free the209// physical memory it refers to.210void211proc_freepagetable(pagetable_t pagetable, uint64 sz)212{213  uvmunmap(pagetable, TRAMPOLINE, 1, 0);214  uvmunmap(pagetable, TRAPFRAME, 1, 0);215  uvmfree(pagetable, sz);216}217218// Set up first user process.219void220userinit(void)221{222  struct proc *p;223224  p = allocproc();225  initproc = p;226  227  p->cwd = namei("/");228229  p->state = RUNNABLE;230231  release(&p->lock);232}233234// Grow or shrink user memory by n bytes.235// Return 0 on success, -1 on failure.236int237growproc(int n)238{239  uint64 sz;240  struct proc *p = myproc();241242  sz = p->sz;243  if(n > 0){244    if(sz + n > TRAPFRAME) {245      return -1;246    }247    if((sz = uvmalloc(p->pagetable, sz, sz + n, PTE_W)) == 0) {248      return -1;249    }250  } else if(n < 0){251    sz = uvmdealloc(p->pagetable, sz, sz + n);252  }253  p->sz = sz;254  return 0;255}256257// Create a new process, copying the parent.258// Sets up child kernel stack to return as if from fork() system call.259int260kfork(void)261{262  int i, pid;263  struct proc *np;264  struct proc *p = myproc();265266  // Allocate process.267  if((np = allocproc()) == 0){268    return -1;269  }270271  // Copy user memory from parent to child.272  if(uvmcopy(p->pagetable, np->pagetable, p->sz) < 0){273    freeproc(np);274    release(&np->lock);275    return -1;276  }277  np->sz = p->sz;278279  // copy saved user registers.280  *(np->trapframe) = *(p->trapframe);281282  // Cause fork to return 0 in the child.283  np->trapframe->a0 = 0;284285  // increment reference counts on open file descriptors.286  for(i = 0; i < NOFILE; i++)287    if(p->ofile[i])288      np->ofile[i] = filedup(p->ofile[i]);289  np->cwd = idup(p->cwd);290291  safestrcpy(np->name, p->name, sizeof(p->name));292293  pid = np->pid;294295  release(&np->lock);296297  acquire(&wait_lock);298  np->parent = p;299  release(&wait_lock);300301  acquire(&np->lock);302  np->state = RUNNABLE;303  release(&np->lock);304305  return pid;306}307308// Pass p's abandoned children to init.309// Caller must hold wait_lock.310void311reparent(struct proc *p)312{313  struct proc *pp;314315  for(pp = proc; pp < &proc[NPROC]; pp++){316    if(pp->parent == p){317      pp->parent = initproc;318      wakeup(initproc);319    }320  }321}322323// Exit the current process.  Does not return.324// An exited process remains in the zombie state325// until its parent calls wait().326void327kexit(int status)328{329  struct proc *p = myproc();330331  if(p == initproc)332    panic("init exiting");333334  // Close all open files.335  for(int fd = 0; fd < NOFILE; fd++){336    if(p->ofile[fd]){337      struct file *f = p->ofile[fd];338      fileclose(f);339      p->ofile[fd] = 0;340    }341  }342343  begin_op();344  iput(p->cwd);345  end_op();346  p->cwd = 0;347348  acquire(&wait_lock);349350  // Give any children to init.351  reparent(p);352353  // Parent might be sleeping in wait().354  wakeup(p->parent);355  356  acquire(&p->lock);357358  p->xstate = status;359  p->state = ZOMBIE;360361  release(&wait_lock);362363  // Jump into the scheduler, never to return.364  sched();365  panic("zombie exit");366}367368// Wait for a child process to exit and return its pid.369// Return -1 if this process has no children.370int371kwait(uint64 addr)372{373  struct proc *pp;374  int havekids, pid;375  struct proc *p = myproc();376377  acquire(&wait_lock);378379  for(;;){380    // Scan through table looking for exited children.381    havekids = 0;382    for(pp = proc; pp < &proc[NPROC]; pp++){383      if(pp->parent == p){384        // make sure the child isn't still in exit() or swtch().385        acquire(&pp->lock);386387        havekids = 1;388        if(pp->state == ZOMBIE){389          // Found one.390          pid = pp->pid;391          if(addr != 0 && copyout(p->pagetable, addr, (char *)&pp->xstate,392                                  sizeof(pp->xstate)) < 0) {393            release(&pp->lock);394            release(&wait_lock);395            return -1;396          }397          freeproc(pp);398          release(&pp->lock);399          release(&wait_lock);400          return pid;401        }402        release(&pp->lock);403      }404    }405406    // No point waiting if we don't have any children.407    if(!havekids || killed(p)){408      release(&wait_lock);409      return -1;410    }411    412    // Wait for a child to exit.413    sleep(p, &wait_lock);  //DOC: wait-sleep414  }415}416417// Per-CPU process scheduler.418// Each CPU calls scheduler() after setting itself up.419// Scheduler never returns.  It loops, doing:420//  - choose a process to run.421//  - swtch to start running that process.422//  - eventually that process transfers control423//    via swtch back to the scheduler.424void425scheduler(void)426{427  struct proc *p;428  struct cpu *c = mycpu();429430  c->proc = 0;431  for(;;){432    // The most recent process to run may have had interrupts433    // turned off; enable them to avoid a deadlock if all434    // processes are waiting. Then turn them back off435    // to avoid a possible race between an interrupt436    // and wfi.437    intr_on();438    intr_off();439440    int found = 0;441    for(p = proc; p < &proc[NPROC]; p++) {442      acquire(&p->lock);443      if(p->state == RUNNABLE) {444        // Switch to chosen process.  It is the process's job445        // to release its lock and then reacquire it446        // before jumping back to us.447        p->state = RUNNING;448        c->proc = p;449        swtch(&c->context, &p->context);450451        // Process is done running for now.452        // It should have changed its p->state before coming back.453        c->proc = 0;454        found = 1;455      }456      release(&p->lock);457    }458    if(found == 0) {459      // nothing to run; stop running on this core until an interrupt.460      asm volatile("wfi");461    }462  }463}464465// Switch to scheduler.  Must hold only p->lock466// and have changed proc->state. Saves and restores467// intena because intena is a property of this468// kernel thread, not this CPU. It should469// be proc->intena and proc->noff, but that would470// break in the few places where a lock is held but471// there's no process.472void473sched(void)474{475  int intena;476  struct proc *p = myproc();477478  if(!holding(&p->lock))479    panic("sched p->lock");480  if(mycpu()->noff != 1)481    panic("sched locks");482  if(p->state == RUNNING)483    panic("sched RUNNING");484  if(intr_get())485    panic("sched interruptible");486487  intena = mycpu()->intena;488  swtch(&p->context, &mycpu()->context);489  mycpu()->intena = intena;490}491492// Give up the CPU for one scheduling round.493void494yield(void)495{496  struct proc *p = myproc();497  acquire(&p->lock);498  p->state = RUNNABLE;499  sched();500  release(&p->lock);501}502503// A fork child's very first scheduling by scheduler()504// will swtch to forkret.505void506forkret(void)507{508  extern char userret[];509  static int first = 1;510  struct proc *p = myproc();511512  // Still holding p->lock from scheduler.513  release(&p->lock);514515  if (first) {516    // File system initialization must be run in the context of a517    // regular process (e.g., because it calls sleep), and thus cannot518    // be run from main().519    fsinit(ROOTDEV);520521    first = 0;522    // ensure other cores see first=0.523    __sync_synchronize();524525    // We can invoke kexec() now that file system is initialized.526    // Put the return value (argc) of kexec into a0.527    p->trapframe->a0 = kexec("/init", (char *[]){ "/init", 0 });528    if (p->trapframe->a0 == -1) {529      panic("exec");530    }531  }532533  // return to user space, mimicing usertrap()'s return.534  prepare_return();535  uint64 satp = MAKE_SATP(p->pagetable);536  uint64 trampoline_userret = TRAMPOLINE + (userret - trampoline);537  ((void (*)(uint64))trampoline_userret)(satp);538}539540// Sleep on channel chan, releasing condition lock lk.541// Re-acquires lk when awakened.542void543sleep(void *chan, struct spinlock *lk)544{545  struct proc *p = myproc();546  547  // Must acquire p->lock in order to548  // change p->state and then call sched.549  // Once we hold p->lock, we can be550  // guaranteed that we won't miss any wakeup551  // (wakeup locks p->lock),552  // so it's okay to release lk.553554  acquire(&p->lock);  //DOC: sleeplock1555  release(lk);556557  // Go to sleep.558  p->chan = chan;559  p->state = SLEEPING;560561  sched();562563  // Tidy up.564  p->chan = 0;565566  // Reacquire original lock.567  release(&p->lock);568  acquire(lk);569}570571// Wake up all processes sleeping on channel chan.572// Caller should hold the condition lock.573void574wakeup(void *chan)575{576  struct proc *p;577578  for(p = proc; p < &proc[NPROC]; p++) {579    if(p != myproc()){580      acquire(&p->lock);581      if(p->state == SLEEPING && p->chan == chan) {582        p->state = RUNNABLE;583      }584      release(&p->lock);585    }586  }587}588589// Kill the process with the given pid.590// The victim won't exit until it tries to return591// to user space (see usertrap() in trap.c).592int593kkill(int pid)594{595  struct proc *p;596597  for(p = proc; p < &proc[NPROC]; p++){598    acquire(&p->lock);599    if(p->pid == pid){600      p->killed = 1;601      if(p->state == SLEEPING){602        // Wake process from sleep().603        p->state = RUNNABLE;604      }605      release(&p->lock);606      return 0;607    }608    release(&p->lock);609  }610  return -1;611}612613void614setkilled(struct proc *p)615{616  acquire(&p->lock);617  p->killed = 1;618  release(&p->lock);619}620621int622killed(struct proc *p)623{624  int k;625  626  acquire(&p->lock);627  k = p->killed;628  release(&p->lock);629  return k;630}631632// Copy to either a user address, or kernel address,633// depending on usr_dst.634// Returns 0 on success, -1 on error.635int636either_copyout(int user_dst, uint64 dst, void *src, uint64 len)637{638  struct proc *p = myproc();639  if(user_dst){640    return copyout(p->pagetable, dst, src, len);641  } else {642    memmove((char *)dst, src, len);643    return 0;644  }645}646647// Copy from either a user address, or kernel address,648// depending on usr_src.649// Returns 0 on success, -1 on error.650int651either_copyin(void *dst, int user_src, uint64 src, uint64 len)652{653  struct proc *p = myproc();654  if(user_src){655    return copyin(p->pagetable, dst, src, len);656  } else {657    memmove(dst, (char*)src, len);658    return 0;659  }660}661662// Print a process listing to console.  For debugging.663// Runs when user types ^P on console.664// No lock to avoid wedging a stuck machine further.665void666procdump(void)667{668  static char *states[] = {669  [UNUSED]    "unused",670  [USED]      "used",671  [SLEEPING]  "sleep ",672  [RUNNABLE]  "runble",673  [RUNNING]   "run   ",674  [ZOMBIE]    "zombie"675  };676  struct proc *p;677  char *state;678679  printf("\n");680  for(p = proc; p < &proc[NPROC]; p++){681    if(p->state == UNUSED)682      continue;683    if(p->state >= 0 && p->state < NELEM(states) && states[p->state])684      state = states[p->state];685    else686      state = "???";687    printf("%d %s %s", p->pid, state, p->name);688    printf("\n");689  }690}691