/* * Copyright (c) 2013 * The President and Fellows of Harvard College. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Process support. * * There is (intentionally) not much here; you will need to add stuff * and maybe change around what's already present. * * p_lock is intended to be held when manipulating the pointers in the * proc structure, not while doing any significant work with the * things they point to. Rearrange this (and/or change it to be a * regular lock) as needed. * * Unless you're implementing multithreaded user processes, the only * process that will have more than one thread is the kernel process. */ #include #include #include #include #include #include #include #include #include #include #include typedef struct list list; typedef struct procs procs; //The process for the kernel; this holds all the kernel-only threads. struct proc * kproc; // The process array procs * processes; // The lock for the above array struct lock * proclock; /* * Mechanism for making the kernel menu thread sleep while processes are running */ #ifdef UW /* count of the number of processes, excluding kproc */ static volatile unsigned int proc_count; /* provides mutual exclusion for proc_count */ /* it would be better to use a lock here, but we use a semaphore because locks are not implemented in the base kernel */ static struct semaphore * proc_count_mutex; /* used to signal the kernel menu thread when there are no processes */ struct semaphore * no_proc_sem; #endif // UW /* * Create a proc structure. */ static struct proc * proc_create(const char * name) { struct proc * proc; proc = kmalloc(sizeof(*proc)); if (proc == NULL) { return NULL; } proc->p_name = kstrdup(name); if (proc->p_name == NULL) { kfree(proc); return NULL; } proc->kids = newlist(); if (!(proc->kids)) { kfree(proc); return NULL; } threadarray_init(&proc->p_threads); spinlock_init(&proc->p_lock); proc->waitlock = lock_create("threadlock"); proc->waiting = cv_create("threadcv"); /* VM fields */ proc->p_addrspace = NULL; /* VFS fields */ proc->p_cwd = NULL; #ifdef UW proc->console = NULL; #endif // UW // My additions proc->pid = 0; proc->parent = NULL; proc->exitcode = -1; return proc; } static procs * create_procs(void) { procs * temp = kmalloc(sizeof(procs)); if (!(temp)) panic("Could not create process list!\n"); temp->lastpid = PID_MIN - 1; return temp; } struct proc * getChild(struct proc * p, int pid) { lock_acquire(proclock); int result = listearch(p->kids, pid); lock_release(proclock); if (!(result)) return NULL; return processes->pids[pid]; } int assignpid(struct proc * proc) { lock_acquire(proclock); for (int i = processes->lastpid + 1; i <= PID_MAX; i++) { if (processes->pids[i] == 0) { proc->pid = i; processes->lastpid = i; processes->pids[i] = proc; lock_release(proclock); return i; } if (i == PID_MAX) { i = PID_MIN - 1; continue; } if (i == processes->lastpid) { lock_release(proclock); return 0; } } lock_release(proclock); return 0; } void add_child(struct proc * parent, int pid) { listsert(parent->kids, pid); } /* static void delete_procs() { kfree(processes->pids); lock_destroy(proclock); }*/ /* * Destroy a proc structure. */ void proc_destroy(struct proc * proc) { KASSERT(proc != NULL); KASSERT(proc != kproc); /* * We don't take p_lock in here because we must have the only * reference to this structure. (Otherwise it would be * incorrect to destroy it.) */ /* VFS fields */ if (proc->p_cwd) { VOP_DECREF(proc->p_cwd); proc->p_cwd = NULL; } #ifndef UW // in the UW version, space destruction occurs in sys_exit, not here if (proc->p_addrspace) { /* * In case p is the currently running process (which * it might be in some circumstances, or if this code * gets moved into exit as suggested above), clear * p_addrspace before calling as_destroy. Otherwise if * as_destroy sleeps (which is quite possible) when we * come back we'll be calling as_activate on a * half-destroyed address space. This tends to be * messily fatal. */ struct addrspace * as; as_deactivate(); as = curproc_setas(NULL); as_destroy(as); } #endif // UW /* #ifdef UW if (proc->console) { vfs_close(proc->console); } #endif // UW */ //threadarray_cleanup(&proc->p_threads); lock_acquire(proclock); processes->pids[proc->pid] = NULL; // update available PIDs lock_release(proclock); lock_destroy(proc->waitlock); cv_destroy(proc->waiting); kfree(proc->p_name); kfree(proc); #ifdef UW /* decrement the process count */ /* note: kproc is not included as_copy in the process count, but proc_destroy is never called on kproc (see KASSERT above), so we're OK to decrement the proc_count unconditionally here */ P(proc_count_mutex); KASSERT(proc_count > 0); proc_count--; /* signal the kernel menu thread if the process count has reached zero */ if (proc_count == 0) { V(no_proc_sem); } V(proc_count_mutex); #endif // UW } /* * Create the process structure for the kernel. */ void proc_bootstrap(void) { processes = create_procs(); kproc = proc_create("[kernel]"); if (kproc == NULL) { panic("proc_create for kproc failed\n"); } #ifdef UW proc_count = 0; proc_count_mutex = sem_create("proc_count_mutex",1); if (proc_count_mutex == NULL) { panic("could not create proc_count_mutex semaphore\n"); } no_proc_sem = sem_create("no_proc_sem",0); if (no_proc_sem == NULL) { panic("could not create no_proc_sem semaphore\n"); } #endif // UW int processesLen = PID_MAX; for (int i = 0; i <= processesLen; i++) { processes->pids[i] = NULL; } proclock = lock_create("proclock"); if (!(proclock)) panic("Process manager lock could not be created!\n"); } /* * Create a fresh proc for use by runprogram. * * It will have no address space and will inherit the current * process's (that is, the kernel menu's) current directory. */ struct proc * proc_create_runprogram(const char * name) { struct proc * proc; char * console_path; proc = proc_create(name); if (proc == NULL) { return NULL; } #ifdef UW /* open the console - this should always succeed */ console_path = kstrdup("con:"); if (console_path == NULL) { panic("unable to copy console path name during process creation\n"); } if (vfs_open(console_path,O_WRONLY,0,&(proc->console))) { panic("unable to open the console during process creation\n"); } kfree(console_path); #endif // UW /* VM fields */ proc->p_addrspace = NULL; /* VFS fields */ #ifdef UW /* we do not need to acquire the p_lock here, the running thread should have the only reference to this process */ /* also, acquiring the p_lock is problematic because VOP_INCREF may block */ if (curproc->p_cwd != NULL) { VOP_INCREF(curproc->p_cwd); proc->p_cwd = curproc->p_cwd; } #else // UW spinlock_acquire(&curproc->p_lock); if (curproc->p_cwd != NULL) { VOP_INCREF(curproc->p_cwd); proc->p_cwd = curproc->p_cwd; } spinlock_release(&curproc->p_lock); #endif // UW #ifdef UW /* increment the count of processes */ /* we are assuming that all procs, including those created by fork(), are created using a call to proc_create_runprogram */ P(proc_count_mutex); proc_count++; if (!(assignpid(proc))) { kfree(proc); return NULL; } V(proc_count_mutex); #endif // UW return proc; } /* * Add a thread to a process. Either the thread or the process might * or might not be current. */ int proc_addthread(struct proc * proc, struct thread * t) { int result; KASSERT(t->t_proc == NULL); spinlock_acquire(&proc->p_lock); result = threadarray_add(&proc->p_threads, t, NULL); spinlock_release(&proc->p_lock); if (result) { return result; } t->t_proc = proc; return 0; } /* * Remove a thread from its process. Either the thread or the process * might or might not be current. */ void proc_remthread(struct thread * t) { struct proc *proc; unsigned i, num; proc = t->t_proc; KASSERT(proc != NULL); spinlock_acquire(&proc->p_lock); /* ugh: find the thread in the array */ num = threadarray_num(&proc->p_threads); for (i=0; ip_threads, i) == t) { threadarray_remove(&proc->p_threads, i); spinlock_release(&proc->p_lock); t->t_proc = NULL; return; } } /* Did not find it. */ spinlock_release(&proc->p_lock); panic("Thread (%p) has escaped from its process (%p)\n", t, proc); } /* * Fetch the address space of the current process. Caution: it isn't * refcounted. If you implement multithreaded processes, make sure to * set up a refcount scheme or some other method to make this safe. */ struct addrspace * curproc_getas(void) { struct addrspace *as; #ifdef UW /* Until user processes are created, threads used in testing * (i.e., kernel threads) have no process or address space. */ if (curproc == NULL) { return NULL; } #endif spinlock_acquire(&curproc->p_lock); as = curproc->p_addrspace; spinlock_release(&curproc->p_lock); return as; } /* * Change the address space of the current process, and return the old * one. */ struct addrspace * curproc_setas(struct addrspace * newas) { struct addrspace * oldas; struct proc * proc = curproc; spinlock_acquire(&proc->p_lock); oldas = proc->p_addrspace; proc->p_addrspace = newas; spinlock_release(&proc->p_lock); return oldas; }