415 lines
9.4 KiB
C
415 lines
9.4 KiB
C
#include "types.h"
|
|
#include "param.h"
|
|
#include "memlayout.h"
|
|
#include "riscv.h"
|
|
#include "spinlock.h"
|
|
#include "proc.h"
|
|
#include "defs.h"
|
|
|
|
struct proc proc[NPROC];
|
|
|
|
struct proc *initproc;
|
|
|
|
extern void forkret(void);
|
|
void freeproc(struct proc *p);
|
|
|
|
extern char trampoline[]; // trampoline.S
|
|
|
|
// helps ensure that wakeups of wait()ing
|
|
// parents are not lost. helps obey the
|
|
// memory model when using p->parent.
|
|
// must be acquired before any p->lock.
|
|
struct spinlock wait_lock;
|
|
|
|
// Allocate a page for each process's kernel stack.
|
|
// Map it high in memory, followed by an invalid
|
|
// guard page.
|
|
void
|
|
proc_mapstacks(pagetable_t kpgtbl)
|
|
{
|
|
struct proc *p;
|
|
|
|
for(p = proc; p < &proc[NPROC]; p++) {
|
|
char *pa = kalloc();
|
|
if(pa == 0)
|
|
panic("kalloc");
|
|
uint64 va = KSTACK((int) (p - proc));
|
|
kvmmap(kpgtbl, va, (uint64)pa, PGSIZE, PTE_R | PTE_W);
|
|
}
|
|
}
|
|
|
|
// initialize the proc table.
|
|
void
|
|
procinit(void)
|
|
{
|
|
struct proc *p;
|
|
|
|
initlock(&wait_lock, "wait_lock");
|
|
for(p = proc; p < &proc[NPROC]; p++) {
|
|
initlock(&p->lock, "proc");
|
|
p->state = UNUSED;
|
|
p->kstack = KSTACK((int) (p - proc));
|
|
}
|
|
}
|
|
|
|
// Look in the process table for an UNUSED proc.
|
|
// If found, initialize state required to run in the kernel,
|
|
// and return with p->lock held.
|
|
// If there are no free procs, or a memory allocation fails, return 0.
|
|
struct proc *allocproc(void);
|
|
|
|
// Create a user page table for a given process, with no user memory,
|
|
// but with trampoline and trapframe pages.
|
|
pagetable_t proc_pagetable(struct proc *p);
|
|
|
|
// Free a process's page table, and free the
|
|
// physical memory it refers to.
|
|
void proc_freepagetable(pagetable_t pagetable, uint64 sz);
|
|
|
|
// a user program that calls exec("/init")
|
|
// assembled from ../user/initcode.S
|
|
// od -t xC ../user/initcode
|
|
uchar initcode[] = {
|
|
0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x45, 0x02,
|
|
0x97, 0x05, 0x00, 0x00, 0x93, 0x85, 0x35, 0x02,
|
|
0x93, 0x08, 0x70, 0x00, 0x73, 0x00, 0x00, 0x00,
|
|
0x93, 0x08, 0x20, 0x00, 0x73, 0x00, 0x00, 0x00,
|
|
0xef, 0xf0, 0x9f, 0xff, 0x2f, 0x69, 0x6e, 0x69,
|
|
0x74, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
// Set up first user process.
|
|
void
|
|
userinit(void)
|
|
{
|
|
struct proc *p;
|
|
|
|
p = allocproc();
|
|
initproc = p;
|
|
|
|
// allocate one user page and copy initcode's instructions
|
|
// and data into it.
|
|
uvmfirst(p->pagetable, initcode, sizeof(initcode));
|
|
p->sz = PGSIZE;
|
|
|
|
// prepare for the very first "return" from kernel to user.
|
|
p->trapframe->epc = 0; // user program counter
|
|
p->trapframe->sp = PGSIZE; // user stack pointer
|
|
|
|
safestrcpy(p->name, "initcode", sizeof(p->name));
|
|
p->cwd = namei("/");
|
|
|
|
p->state = RUNNABLE;
|
|
|
|
release(&p->lock);
|
|
}
|
|
|
|
// Create a new process, copying the parent.
|
|
// Sets up child kernel stack to return as if from fork() 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.
|
|
if(uvmcopy(p->pagetable, np->pagetable, p->sz) < 0){
|
|
freeproc(np);
|
|
release(&np->lock);
|
|
return -1;
|
|
}
|
|
np->sz = p->sz;
|
|
|
|
// copy saved user registers.
|
|
*(np->trapframe) = *(p->trapframe);
|
|
|
|
// Cause fork to return 0 in the child.
|
|
np->trapframe->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;
|
|
|
|
release(&np->lock);
|
|
|
|
acquire(&wait_lock);
|
|
np->parent = p;
|
|
release(&wait_lock);
|
|
|
|
acquire(&np->lock);
|
|
np->state = RUNNABLE;
|
|
release(&np->lock);
|
|
|
|
return pid;
|
|
}
|
|
|
|
// Pass p's abandoned children to init.
|
|
// Caller must hold wait_lock.
|
|
void reparent(struct proc *p);
|
|
|
|
// Exit the current process. Does not return.
|
|
// An exited process remains in the zombie state
|
|
// until its parent calls wait().
|
|
void
|
|
exit(int status)
|
|
{
|
|
struct proc *p = myproc();
|
|
|
|
if(p == initproc)
|
|
panic("init exiting");
|
|
|
|
// Close all open files.
|
|
for(int fd = 0; fd < NOFILE; fd++){
|
|
if(p->ofile[fd]){
|
|
struct file *f = p->ofile[fd];
|
|
fileclose(f);
|
|
p->ofile[fd] = 0;
|
|
}
|
|
}
|
|
|
|
begin_op();
|
|
iput(p->cwd);
|
|
end_op();
|
|
p->cwd = 0;
|
|
|
|
acquire(&wait_lock);
|
|
|
|
// Give any children to init.
|
|
reparent(p);
|
|
|
|
// Parent might be sleeping in wait().
|
|
wakeup(p->parent);
|
|
|
|
acquire(&p->lock);
|
|
|
|
p->xstate = status;
|
|
p->state = ZOMBIE;
|
|
|
|
release(&wait_lock);
|
|
|
|
// Jump into the scheduler, never to return.
|
|
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(uint64 addr)
|
|
{
|
|
struct proc *pp;
|
|
int havekids, pid;
|
|
struct proc *p = myproc();
|
|
|
|
acquire(&wait_lock);
|
|
|
|
for(;;){
|
|
// Scan through table looking for exited children.
|
|
havekids = 0;
|
|
for(pp = proc; pp < &proc[NPROC]; pp++){
|
|
if(pp->parent == p){
|
|
// make sure the child isn't still in exit() or swtch().
|
|
acquire(&pp->lock);
|
|
|
|
havekids = 1;
|
|
if(pp->state == ZOMBIE){
|
|
// Found one.
|
|
pid = pp->pid;
|
|
if(addr != 0 && copyout(p->pagetable, addr, (char *)&pp->xstate,
|
|
sizeof(pp->xstate)) < 0) {
|
|
release(&pp->lock);
|
|
release(&wait_lock);
|
|
return -1;
|
|
}
|
|
freeproc(pp);
|
|
release(&pp->lock);
|
|
release(&wait_lock);
|
|
return pid;
|
|
}
|
|
release(&pp->lock);
|
|
}
|
|
}
|
|
|
|
// No point waiting if we don't have any children.
|
|
if(!havekids || killed(p)){
|
|
release(&wait_lock);
|
|
return -1;
|
|
}
|
|
|
|
// Wait for a child to exit.
|
|
sleep_lock(p, &wait_lock); // DOC: wait-sleep
|
|
}
|
|
}
|
|
|
|
// 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(;;){
|
|
// Avoid deadlock by ensuring that devices can interrupt.
|
|
intr_on();
|
|
|
|
for(p = proc; p < &proc[NPROC]; p++) {
|
|
acquire(&p->lock);
|
|
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(&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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Switch to scheduler. Must hold only p->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);
|
|
|
|
// A fork child's very first scheduling by scheduler()
|
|
// will swtch to forkret.
|
|
void
|
|
forkret(void)
|
|
{
|
|
static int 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);
|
|
}
|
|
|
|
usertrapret();
|
|
}
|
|
|
|
// Atomically release lock and sleep on chan.
|
|
// Reacquires lock when awakened.
|
|
void sleep_lock(void *chan, struct spinlock *lk);
|
|
|
|
// Wake up all processes sleeping on chan.
|
|
// Must be called without any p->lock.
|
|
void wakeup(void *chan)
|
|
{
|
|
struct proc *p;
|
|
|
|
for(p = proc; p < &proc[NPROC]; p++) {
|
|
if(p != myproc()){
|
|
acquire(&p->lock);
|
|
if(p->state == SLEEPING && p->chan == chan) {
|
|
p->state = RUNNABLE;
|
|
}
|
|
release(&p->lock);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Kill the process with the given pid.
|
|
// The victim won't exit until it tries to return
|
|
// to user space (see usertrap() in trap.c).
|
|
int kill(int pid);
|
|
void setkilled(struct proc *p);
|
|
int killed(struct proc *p);
|
|
|
|
// 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",
|
|
[USED] "used",
|
|
[SLEEPING] "sleep ",
|
|
[RUNNABLE] "runble",
|
|
[RUNNING] "run ",
|
|
[ZOMBIE] "zombie"
|
|
};
|
|
struct proc *p;
|
|
char *state;
|
|
|
|
printstr("\n");
|
|
for(p = proc; p < &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 = "???";
|
|
|
|
printint(p->pid);
|
|
printstr(" ");
|
|
printstr(state);
|
|
printstr(" ");
|
|
printstr(p->name);
|
|
printstr("\n");
|
|
}
|
|
}
|