void sys__exit(int exitcode) { struct addrspace* as; struct proc* p = curproc; DEBUG(DB_EXEC, "sys_exit(): process %u exiting with code %d\n",p->p_pid,exitcode); KASSERT(p->p_addrspace != NULL); as_deactivate(); as = proc_setas(NULL); as_destroy(as); proc_remthread(curthread); p->p_exitstatus = _MKWAIT_EXIT(exitcode); p->p_exitable = true; lock_acquire(p->p_waitpid_lk); cv_broadcast(p->p_waitpid_cv, p->p_waitpid_lk); lock_release(p->p_waitpid_lk); proc_destroy(p); thread_exit(); panic("sys__exit(): unexpected return from thread_exit()\n"); }
void sys__exit(int exitcode) { struct addrspace *as; struct proc *p = curproc; /* for now, just include this to keep the compiler from complaining about an unused variable */ (void)exitcode; DEBUG(DB_SYSCALL,"Syscall: _exit(%d)\n",exitcode); KASSERT(curproc->p_addrspace != NULL); as_deactivate(); /* * 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. */ as = curproc_setas(NULL); as_destroy(as); /* detach this thread from its process */ /* note: curproc cannot be used after this call */ proc_remthread(curthread); /* if this is the last user process in the system, proc_destroy() will wake up the kernel menu thread */ proc_destroy(p); thread_exit(); /* thread_exit() does not return, so we should never get here */ panic("return from thread_exit in sys_exit\n"); }
/* this needs to be fixed to get exit() and waitpid() working properly */ void sys__exit(int exitcode, int type) { struct addrspace *as; struct proc *p = curproc; DEBUG(DB_SYSCALL,"Syscall: _exit(%d)\n",exitcode); KASSERT(curproc->p_addrspace != NULL); as_deactivate(); /* * 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. */ as = curproc_setas(NULL); as_destroy(as); lock_acquire(p->proc_exit_lock); if(!p->proc_parent_exited && p->pid > 1){ // Parent didnt exit yet, so we must only semi-destroy the proc proc_set_exit_status(p,exitcode, type); cv_broadcast(p->proc_exit_cv, p->proc_exit_lock); proc_exited_signal(p); /* detach this thread from its process */ /* note: curproc cannot be used after this call */ proc_remthread(curthread); // semi_destroy will release the proc_exit_lock for us. proc_semi_destroy(p); lock_release(p->proc_exit_lock); }else{ proc_exited_signal(p); lock_release(p->proc_exit_lock); /* detach this thread from its process */ /* note: curproc cannot be used after this call */ proc_remthread(curthread); /* if this is the last user process in the system, proc_destroy() will wake up the kernel menu thread */ proc_destroy(p); } thread_exit(); /* thread_exit() does not return, so we should never get here */ panic("return from thread_exit in sys_exit\n"); }
/* void free_root(struct procinfo* pi){ KASSERT(pi != NULL); if (pi->parent_pid == -1){ array_set(proc) } }*/ void sys__exit(int exitcode) { struct addrspace *as; struct proc *p = curproc; /* for now, just include this to keep the compiler from complaining about an unused variable */ #if OPT_A2 struct procinfo *pi = array_get(procinfotable, p->pid-1); if(pi == NULL){ goto parentexited; } lock_acquire(p->p_waitpid_lock); pi->exit_code = _MKWAIT_EXIT(exitcode); pi->active = 0; cv_broadcast(pi->waitpid_cv,p->p_waitpid_lock); lock_release(p->p_waitpid_lock); free_children(p->pid); parentexited: #else (void)exitcode; #endif DEBUG(DB_SYSCALL,"Syscall: _exit(%d)\n",exitcode); KASSERT(curproc->p_addrspace != NULL); as_deactivate(); /* * 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. */ as = curproc_setas(NULL); as_destroy(as); /* detach this thread from its process */ /* note: curproc cannot be used after this call */ proc_remthread(curthread); /* if this is the last user process in the system, proc_destroy() will wake up the kernel menu thread */ proc_destroy(p); thread_exit(); /* thread_exit() does not return, so we should never get here */ panic("return from thread_exit in sys_exit\n"); }
/* * Destroy a proc structure. * * Note: nothing currently calls this. Your wait/exit code will * probably want to do so. */ void proc_destroy(struct proc *proc) { /* * You probably want to destroy and null out much of the * process (particularly the address space) at exit time if * your wait/exit design calls for the process structure to * hang around beyond process exit. Some wait/exit designs * do, some don't. */ 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; } /* VM fields */ 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); } threadarray_cleanup(&proc->p_threads); spinlock_cleanup(&proc->p_lock); kfree(proc->p_name); kfree(proc); }
void sys__exit(int exitcode) { struct addrspace *as; struct proc *p = curproc; #if OPT_A2 int pid = (int)curproc->pid; struct proc_entry *entry = process_table[pid]; lock_acquire(entry->exit_mutex); process_table[pid]->exited = 1; process_table[pid]->exitcode = exitcode; //if(lock_do_i_hold(entry->exit_mutex)) cv_signal(entry->exit_wait, entry->exit_mutex); lock_release(entry->exit_mutex); #else /* for now, just include this to keep the compiler from complaining about an unused variable */ (void)exitcode; #endif kprintf("is exiting: %d\n", pid); DEBUG(DB_SYSCALL,"Syscall: _exit(%d)\n",exitcode); KASSERT(curproc->p_addrspace != NULL); as_deactivate(); /* * 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. */ as = curproc_setas(NULL); as_destroy(as); /* detach this thread from its process */ /* note: curproc cannot be used after this call */ proc_remthread(curthread); /* if this is the last user process in the system, proc_destroy() will wake up the kernel menu thread */ proc_destroy(p); thread_exit(); /* thread_exit() does not return, so we should never get here */ panic("return from thread_exit in sys_exit\n"); }
/* * Destroy a proc structure. */ void proc_destroy(struct proc *proc) { /* * note: some parts of the process structure, such as the address space, * are destroyed in sys_exit, before we get here * * note: depending on where this function is called from, curproc may not * be defined because the calling thread may have already detached itself * from the process. */ KASSERT(proc != NULL); KASSERT(proc != kproc); #if OPT_A2 lock_destroy(proc->opencloselock); if(proc->filetable != NULL) { for(int i = 127; i >= 0; i--) { if(array_get(proc->filetable, i) != NULL) { //kprintf("%d \n", i); cleanupfile(array_get(proc->filetable, i)); } array_remove(proc->filetable, i); } array_destroy(proc->filetable); } #endif /* * 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); spinlock_cleanup(&proc->p_lock); kfree(proc->p_name); kfree(proc); #ifdef UW /* decrement the process count */ /* note: kproc is not included 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 }
/* * Load program "progname" and start running it in usermode. * Does not return except on error. * * Calls vfs_open on progname and thus may destroy it. */ int runprogram(char *progname, char **args, unsigned int nargs) { struct addrspace *as; struct vnode *v; vaddr_t entrypoint, stackptr; int result; int argc = nargs; char **argv = (char **)kmalloc((argc + 1) * sizeof(char *)); if(argv == NULL) { return ENOMEM; } for(int i = 0; i < argc; ++i) { argv[i] = args[i]; } argv[argc] = NULL; /* Open the file. */ result = vfs_open(progname, O_RDONLY, 0, &v); if (result) { return result; } /* We should be a new process. */ KASSERT(curproc_getas() == NULL); /* Create a new address space. */ as = as_create(); if (as ==NULL) { vfs_close(v); return ENOMEM; } /* Switch to it and activate it. */ curproc_setas(as); as_activate(); /* Load the executable. */ result = load_elf(v, &entrypoint); if (result) { /* p_addrspace will go away when curproc is destroyed */ vfs_close(v); return result; } /* Done with the file now. */ vfs_close(v); /* Define the user stack in the address space */ result = as_define_stack(as, &stackptr); if (result) { /* p_addrspace will go away when curproc is destroyed */ return result; } /*------copy args to user stack-----------*/ vaddr_t *argPtrs = (vaddr_t *)kmalloc((argc + 1) * sizeof(vaddr_t)); if(argPtrs == NULL) { for(int i = 0; i < argc; ++i) { kfree(argv[i]); } kfree(argv); as_deactivate(); as = curproc_setas(NULL); as_destroy(as); return ENOMEM; } for(int i = argc-1; i >= 0; --i) { //arg length with null size_t curArgLen = strlen(argv[i]) + 1; size_t argLen = ROUNDUP(curArgLen,4); stackptr -= (argLen * sizeof(char)); //kprintf("copying arg: %s to addr: %p\n", temp, (void *)stackptr); //copy to stack result = copyout((void *) argv[i], (userptr_t)stackptr, curArgLen); if(result) { kfree(argPtrs); for(int i = 0; i < argc; ++i) { kfree(argv[i]); } kfree(argv); as_deactivate(); as = curproc_setas(NULL); as_destroy(as); return result; } argPtrs[i] = stackptr; } argPtrs[argc] = (vaddr_t)NULL; //copy arg pointers for(int i = argc; i >= 0; --i) { stackptr -= sizeof(vaddr_t); result = copyout((void *) &argPtrs[i], ((userptr_t)stackptr),sizeof(vaddr_t)); if(result) { kfree(argPtrs); for(int i = 0; i < argc; ++i) { kfree(argv[i]); } kfree(argv); as_deactivate(); as = curproc_setas(NULL); as_destroy(as); return result; } } kfree(argPtrs); vaddr_t baseAddress = USERSTACK; vaddr_t argvPtr = stackptr; vaddr_t offset = ROUNDUP(USERSTACK - stackptr,8); stackptr = baseAddress - offset; /* for(vaddr_t i = baseAddress; i >= stackptr; --i) { char *temp; temp = (char *)i; //kprintf("%p: %c\n",(void *)i,*temp); kprintf("%p: %x\n", (void *)i, *temp & 0xff); } */ /*-done-copy args to user stack-----------*/ /* Warp to user mode. */ enter_new_process(argc /*argc*/, (userptr_t)argvPtr /*userspace addr of argv*/, stackptr, entrypoint); /* enter_new_process does not return. */ panic("enter_new_process returned\n"); return EINVAL; }
/* * Function called when user-level code hits a fatal fault. */ static void kill_curthread(vaddr_t epc, unsigned code, vaddr_t vaddr) { int sig = 0; KASSERT(code < NTRAPCODES); switch (code) { case EX_IRQ: case EX_IBE: case EX_DBE: case EX_SYS: /* should not be seen */ KASSERT(0); sig = SIGABRT; break; case EX_MOD: case EX_TLBL: case EX_TLBS: sig = SIGSEGV; break; case EX_ADEL: case EX_ADES: sig = SIGBUS; break; case EX_BP: sig = SIGTRAP; break; case EX_RI: sig = SIGILL; break; case EX_CPU: sig = SIGSEGV; break; case EX_OVF: sig = SIGFPE; break; } /* * You will probably want to change this. */ #if OPT_A3 (void)epc; (void)vaddr; struct addrspace *as; struct proc *p = curproc; int parentLocation = locatePid(p->p_pid); struct procStruct *parentProcStr = array_get(procStructArray, parentLocation); parentProcStr->exitcode = _MKWAIT_SIG(sig); cleanChildren(parentLocation); V(parentProcStr->proc_sem); KASSERT(curproc->p_addrspace != NULL); as_deactivate(); /* * 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. */ as = curproc_setas(NULL); as_destroy(as); /* detach this thread from its process */ /* note: curproc cannot be used after this call */ proc_remthread(curthread); /* if this is the last user process in the system, proc_destroy() will wake up the kernel menu thread */ proc_destroy(p); thread_exit(); /* thread_exit() does not return, so we should never get here */ panic("return from thread_exit in sys_exit\n"); #else kprintf("Fatal user mode trap %u sig %d (%s, epc 0x%x, vaddr 0x%x)\n", code, sig, trapcodenames[code], epc, vaddr); panic("I don't know how to handle this\n"); #endif }
/* * Function called when user-level code hits a fatal fault. */ static void kill_curthread(vaddr_t epc, unsigned code, vaddr_t vaddr) { int sig = 0; KASSERT(code < NTRAPCODES); switch (code) { case EX_IRQ: case EX_IBE: case EX_DBE: case EX_SYS: /* should not be seen */ KASSERT(0); sig = SIGABRT; break; case EX_MOD: case EX_TLBL: case EX_TLBS: sig = SIGSEGV; break; case EX_ADEL: case EX_ADES: sig = SIGBUS; break; case EX_BP: sig = SIGTRAP; break; case EX_RI: sig = SIGILL; break; case EX_CPU: sig = SIGSEGV; break; case EX_OVF: sig = SIGFPE; break; } /* * You will probably want to change this. */ #if OPT_A3 //supposed to be similar to sys_exit (void)epc; (void)vaddr; struct addrspace *as; struct proc *p = curproc; as_deactivate(); as = curproc_setas(NULL); as_destroy(as); proc_remthread(curthread); proc_destroy(p); thread_exit(); panic("Should not have returned here!\n"); #else kprintf("Fatal user mode trap %u sig %d (%s, epc 0x%x, vaddr 0x%x)\n", code, sig, trapcodenames[code], epc, vaddr); panic("I don't know how to handle this\n"); #endif }
// man page // int execv(const char *program, char **args); int sys_execv(char *progname, char **argv){ struct addrspace *as; struct vnode *v; vaddr_t entrypoint, stackptr; int result; unsigned long argc = 0; /* Open the file. */ result = vfs_open(progname, O_RDONLY, 0, &v); if (result) { return result; } /* Destroy the current address space */ as_deactivate(); as = curproc_setas(NULL); as_destroy(as); /* We should be a new process. */ KASSERT(curproc_getas() == NULL); /* Create a new address space. */ as = as_create(); if (as ==NULL) { vfs_close(v); return ENOMEM; } /* Switch to it and activate it. */ curproc_setas(as); as_activate(); /* Load the executable. */ result = load_elf(v, &entrypoint); if (result) { /* p_addrspace will go away when curproc is destroyed */ vfs_close(v); return result; } /* Done with the file now. */ vfs_close(v); /* Define the user stack in the address space */ result = as_define_stack(as, &stackptr); if (result) { /* p_addrspace will go away when curproc is destroyed */ return result; } // we need to figure out what argc would be for(unsigned long i = 0; argv[i] != NULL; i++){ argc = i + 1; } #if OPT_A2 // How much space do we need for args in stack? // We need space for each pointer (4argc) // We need space for each character, including NULL termination, plus // padding to make multiples of 4 int stringSpace = 0; for (unsigned long i = 0; i < argc; i++) { stringSpace += strlen(argv[i]) + 1; } // Align stack pointer to an 8-byte alignment while ((stackptr - (4*argc) - stringSpace) % 8 != 0) { stackptr--; } // Use a vaddr array to track the addresses the strings end up in // One bigger than argc to also have the pointer to the final NULL // value vaddr_t stringAddr[argc+1]; // Copy argument strings onto stack // Array must end with NULL pointer, so do that first stackptr -= 4; copyout((void *)NULL, (userptr_t)stackptr, (size_t)4); stringAddr[argc] = stackptr; for (int i = argc-1; i >= 0; i--) { stackptr -= strlen(argv[i]) + 1; while (stackptr % 4 != 0) stackptr--; copyoutstr(argv[i], (userptr_t)stackptr, (size_t)strlen(argv[i]), NULL); stringAddr[i] = stackptr; } // Now use the stored addresses of the string to add the appropriate // pointers to the stack for (int i = argc; i >= 0; i--) { stackptr -= sizeof(vaddr_t); copyout(&stringAddr[i], (userptr_t)stackptr, sizeof(vaddr_t)); } /* Warp to user mode. */ enter_new_process(argc /*argc*/, (userptr_t)stackptr /*userspace addr of argv*/, stackptr, entrypoint); #else /* Warp to user mode. */ enter_new_process(0 /*argc*/, NULL /*userspace addr of argv*/, stackptr, entrypoint); #endif /* enter_new_process does not return. */ panic("enter_new_process returned\n"); return EINVAL; (void)progname; (void)argv; }
/* * Destroy a proc structure. * * Note: nothing currently calls this. Your wait/exit code will * probably want to do so. */ void proc_destroy(struct proc *proc) { /* * You probably want to destroy and null out much of the * process (particularly the address space) at exit time if * your wait/exit design calls for the process structure to * hang around beyond process exit. Some wait/exit designs * do, some don't. */ 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; } if (proc->p_filetable) { filetable_destroy(proc->p_filetable); proc->p_filetable = NULL; } /* VM fields */ if (proc->p_addrspace) { /* * If p is the current process, remove it safely from * p_addrspace before destroying it. This makes sure * we don't try to activate the address space while * it's being destroyed. * * Also explicitly deactivate, because setting the * address space to NULL won't necessarily do that. * * (When the address space is NULL, it means the * process is kernel-only; in that case it is normally * ok if the MMU and MMU- related data structures * still refer to the address space of the last * process that had one. Then you save work if that * process is the next one to run, which isn't * uncommon. However, here we're going to destroy the * address space, so we need to make sure that nothing * in the VM system still refers to it.) * * The call to as_deactivate() must come after we * clear the address space, or a timer interrupt might * reactivate the old address space again behind our * back. * * If p is not the current process, still remove it * from p_addrspace before destroying it as a * precaution. Note that if p is not the current * process, in order to be here p must either have * never run (e.g. cleaning up after fork failed) or * have finished running and exited. It is quite * incorrect to destroy the proc structure of some * random other process while it's still running... */ struct addrspace *as; if (proc == curproc) { as = proc_setas(NULL); as_deactivate(); } else { as = proc->p_addrspace; proc->p_addrspace = NULL; } as_destroy(as); } threadarray_cleanup(&proc->p_threads); spinlock_cleanup(&proc->p_lock); kfree(proc->p_name); kfree(proc); }
int sys_execv(char* program, char** args) { int result; char name[strlen(program) + 1]; result = copyin((userptr_t) program, name, (strlen(program) + 1) * sizeof(char)); //kprintf("%s", name); int len_arg = 0; while(args[len_arg] != NULL) { len_arg++; } int length_every_arg[len_arg]; for(int i = 0; i < len_arg; i++) { length_every_arg[i] = strlen(args[i]); } char* kern_args[len_arg]; for(int i = 0; i < len_arg; i++) { kern_args[i] = kmalloc(sizeof(char) * length_every_arg[i] + 1); copyin((const_userptr_t)args[i], kern_args[i], (length_every_arg[i] + 1) * sizeof(char)); } /* for(int i = 0; i < len_arg; i++) { kprintf("{ _%d__%p__%p_",strlen(kern_args[i]), kern_args[i],args[i]); kprintf("%s", kern_args[i]); kprintf("}\n"); } */ ///////////////////////////////////////////////////// // this is "copied" from runprogram struct addrspace* old_as; struct vnode* v; vaddr_t entrypoint, stackptr; result = vfs_open(program, O_RDONLY, 0, &v); if(result) { return result; } as_deactivate(); old_as = curproc_setas(NULL); as_destroy(old_as); struct addrspace* new_as = as_create(); if(new_as == NULL) { vfs_close(v); return ENOMEM; } curproc_setas(new_as); as_activate(); result = load_elf(v, &entrypoint); if(result) { vfs_close(v); return result; } vfs_close(v); result = as_define_stack(new_as, &stackptr); if(result) { return result; } ////////////////////////////////////////////////////////// // kern_args // the addr of stackptr is 0x8000 0000 vaddr_t argv = stackptr; for(int i = 0; i < len_arg + 1; i++) { argv = argv - 4; } vaddr_t start = argv; vaddr_t temp = argv; copyout(NULL, (userptr_t)(stackptr - 4), 4); //starts from 1, because argv[0] is reserved for the program name for(int i = 0; i < len_arg; i++) { //question? why do I have to add 2 int m = sizeof(char) * (strlen(kern_args[i]) + 1); // kprintf("the value of m -> %d\n", m); argv = argv - m; copyout(kern_args[i], (userptr_t)argv, m); // kprintf("***(%d)%s(%p)\n***", m, (char* )argv, (void *) argv); copyout(&argv, (userptr_t)temp, sizeof(char* )); temp = temp + 4; } for(int i = 0; i < len_arg; i++) { kfree(kern_args[i]); } while(argv % 8 != 0) {argv--;} enter_new_process(len_arg, (userptr_t)start, (vaddr_t) argv, entrypoint); // enter_new_process(0, NULL, stackptr, entrypoint); panic("enter_new_process returned"); return EINVAL; }