diff options
-rw-r--r-- | src/common/include/proc.h | 7 | ||||
-rw-r--r-- | src/kernel/core/thread.c | 55 | ||||
-rw-r--r-- | src/kernel/core/worker.c | 1 | ||||
-rw-r--r-- | src/kernel/include/process.h | 3 | ||||
-rw-r--r-- | src/kernel/user/process.c | 158 | ||||
-rw-r--r-- | src/kernel/user/syscall.c | 4 |
6 files changed, 199 insertions, 29 deletions
diff --git a/src/common/include/proc.h b/src/common/include/proc.h index 01a6722..af50001 100644 --- a/src/common/include/proc.h +++ b/src/common/include/proc.h @@ -5,10 +5,15 @@ #define PS_LOADING 1 #define PS_RUNNING 2 -#define PS_DONE 3 +#define PS_FINISHED 3 #define PS_FAILURE 4 // exception or segfault or stuff #define PS_KILLED 5 +#define FAIL_EXCEPTION 1 // unhandled processor exception +#define FAIL_ZEROPTR 2 // segfault at < 4k +#define FAIL_SEGFAULT 3 +#define FAIL_SC_SEGFAULT 4 // failed to validate parameter for system call + typedef struct { int pid; int state; // one of PS_* diff --git a/src/kernel/core/thread.c b/src/kernel/core/thread.c index 02e81e1..63523af 100644 --- a/src/kernel/core/thread.c +++ b/src/kernel/core/thread.c @@ -88,6 +88,21 @@ thread_t* dequeue_thread() { return t; } +void remove_thread_from_queue(thread_t *t) { + if (queue_first_thread == t) { + queue_first_thread = t->next_in_queue; + if (queue_first_thread == 0) queue_last_thread = 0; + } else { + for (thread_t *it = queue_first_thread; it != 0; it = it->next_in_queue) { + if (it->next_in_queue == t) { + it->next_in_queue = t->next_in_queue; + if (it->next_in_queue == 0) queue_last_thread = t; + break; + } + } + } +} + // ================ // // THE TASKING CODE // // ================ // @@ -118,6 +133,9 @@ void run_scheduler() { static void run_thread(void (*entry)(void*), void* data) { ASSERT(current_thread->state == T_STATE_RUNNING); + dbg_printf("Begin thread 0x%p (in process %d)\n", + current_thread, (current_thread->proc ? current_thread->proc->pid : 0)); + switch_pagedir(get_kernel_pagedir()); asm volatile("sti"); @@ -166,15 +184,24 @@ thread_t *new_thread(entry_t entry, void* data) { t->last_ran = 0; t->current_pd_d = get_kernel_pagedir(); + t->critical_level = CL_USER; // used by user processes t->proc = 0; + t->next_in_proc = 0; t->user_ex_handler = 0; - t->critical_level = CL_USER; return t; } +static void delete_thread(thread_t *t) { + if (t->proc != 0) + process_thread_deleted(t); + + region_free_unmap_free(t->stack_region->addr); + free(t); +} + // ========== // // SETUP CODE // // ========== // @@ -227,8 +254,20 @@ void usleep(int usecs) { } void exit() { + void delete_thread_v(void* v) { + delete_thread((thread_t*)v); + } + + int st = enter_critical(CL_NOSWITCH); + // the critical section here does not guarantee that worker_push will return immediately + // (it may switch before adding the delete_thread task), but once the task is added + // no other switch may happen, therefore this thread will not get re-enqueued + + worker_push(delete_thread_v, current_thread); current_thread->state = T_STATE_FINISHED; - // TODO : add job for deleting the thread, or whatever + + exit_critical(st); + yield(); // expected never to return! ASSERT(false); } @@ -249,4 +288,16 @@ bool resume_thread(thread_t *thread) { return ret; } +void kill_thread(thread_t *thread) { + ASSERT(thread != current_thread); + + int st = enter_critical(CL_NOSWITCH); + + thread->state = T_STATE_FINISHED; + remove_thread_from_queue(thread); + delete_thread(thread); + + exit_critical(st); +} + /* vim: set ts=4 sw=4 tw=0 noet :*/ diff --git a/src/kernel/core/worker.c b/src/kernel/core/worker.c index 4f37b22..e50085a 100644 --- a/src/kernel/core/worker.c +++ b/src/kernel/core/worker.c @@ -36,6 +36,7 @@ void start_workers(int n) { nworkers = n; for (int i = 0; i < n; i++) { workers[i] = new_thread(worker_thread, 0); + dbg_printf("New worker thread: 0x%p\n", workers[i]); } } diff --git a/src/kernel/include/process.h b/src/kernel/include/process.h index 82f1b4d..1b4012f 100644 --- a/src/kernel/include/process.h +++ b/src/kernel/include/process.h @@ -52,6 +52,7 @@ typedef struct process { uint64_t last_ran; int pid; + int status, exit_code; struct process *parent; struct process *next_child; struct process *children; @@ -74,7 +75,7 @@ bool start_process(process_t *p, proc_entry_t entry); // maps a region for user void process_exit(process_t *p, int status, int exit_code); // exit current process bool process_new_thread(process_t *p, proc_entry_t entry, void* sp); -void process_thread_exited(thread_t *t); // called by threading code when a thread exits +void process_thread_deleted(thread_t *t); // called by threading code when a thread exits process_t *process_find_child(process_t *p, int pid); void process_get_status(process_t *p, proc_status_t *st); diff --git a/src/kernel/user/process.c b/src/kernel/user/process.c index 649c801..af7d86f 100644 --- a/src/kernel/user/process.c +++ b/src/kernel/user/process.c @@ -5,7 +5,7 @@ #include <frame.h> #include <process.h> #include <freemem.h> - +#include <worker.h> static int next_pid = 1; @@ -22,6 +22,11 @@ typedef struct { void *sp; } setup_data_t; +typedef struct { + process_t *proc; + int status, exit_code; +} exit_data_t; + // ============================== // // CREATING AND RUNNING PROCESSES // // ============================== // @@ -48,6 +53,7 @@ process_t *new_process(process_t *parent) { proc->pid = (next_pid++); proc->parent = parent; proc->next_fd = 1; + proc->status = PS_LOADING; return proc; @@ -62,15 +68,15 @@ error: static void run_user_code(void* param) { setup_data_t *d = (setup_data_t*)param; + void* esp = d->sp; + proc_entry_t entry = d->entry; + free(d); + process_t *proc = current_thread->proc; ASSERT(proc != 0); switch_pagedir(proc->pd); - void* esp = d->sp; - proc_entry_t entry = d->entry; - free(d); - asm volatile(" \ cli; \ \ @@ -91,18 +97,6 @@ static void run_user_code(void* param) { iret \ "::"b"(esp),"c"(entry)); } -bool start_process(process_t *p, void* entry) { - bool stack_ok = mmap(p, (void*)USERSTACK_ADDR, USERSTACK_SIZE, MM_READ | MM_WRITE); - if (!stack_ok) return false; - - bool ok = process_new_thread(p, entry, (void*)USERSTACK_ADDR + USERSTACK_SIZE); - if (!ok) { - munmap(p, (void*)USERSTACK_ADDR); - return false; - } - - return true; -} bool process_new_thread(process_t *p, proc_entry_t entry, void* sp) { setup_data_t *d = (setup_data_t*)malloc(sizeof(setup_data_t)); @@ -117,7 +111,7 @@ bool process_new_thread(process_t *p, proc_entry_t entry, void* sp) { th->proc = p; th->user_ex_handler = proc_user_exception; - { int st = enter_critical(CL_NOINT); // it's a bit complicated to use mutexes on process_t (TODO: think) + { int st = enter_critical(CL_NOSWITCH); // it's a bit complicated to use mutexes on process_t th->next_in_proc = p->threads; p->threads = th; @@ -129,6 +123,124 @@ bool process_new_thread(process_t *p, proc_entry_t entry, void* sp) { return true; } +bool start_process(process_t *p, void* entry) { + bool stack_ok = mmap(p, (void*)USERSTACK_ADDR, USERSTACK_SIZE, MM_READ | MM_WRITE); + if (!stack_ok) return false; + + bool ok = process_new_thread(p, entry, (void*)USERSTACK_ADDR + USERSTACK_SIZE); + if (!ok) { + munmap(p, (void*)USERSTACK_ADDR); + return false; + } + + p->status = PS_RUNNING; + + return true; +} + +void process_exit(process_t *p, int status, int exit_code) { + // --- Make sure we are not running in a thread we are about to kill + + void process_exit_v(void* args) { + exit_data_t *d = (exit_data_t*)args; + process_exit(d->proc, d->status, d->exit_code); + free(d); + } + + if (current_process() == p) { + exit_data_t *d = (exit_data_t*)malloc(sizeof(exit_data_t)); + + d->proc = p; + d->status = status; + d->exit_code = exit_code; + + worker_push(process_exit_v, d); + pause(); + } + + // ---- Now we can do the actual cleanup + + int st = enter_critical(CL_NOSWITCH); + + p->status = status; + p->exit_code = exit_code; + + while (p->threads != 0) { + thread_t *t = p->threads; + p->threads = p->threads->next_in_proc; + + t->proc = 0; + kill_thread(t); + } + + // release file descriptors + void release_fd(void* a, void* fd) { + unref_file((fs_handle_t*)fd); + } + hashtbl_iter(p->files, release_fd); + delete_hashtbl(p->files); + p->files = 0; + + // unmap memory + while (p->regions != 0) { + munmap(p, p->regions->addr); + } + ASSERT(btree_count(p->regions_idx) == 0); + delete_btree(p->regions_idx); + p->regions_idx = 0; + + // release filesystems + void release_fs(void* a, void* fs) { + unref_fs((fs_t*)fs); + } + hashtbl_iter(p->filesystems, release_fs); + delete_hashtbl(p->filesystems); + p->filesystems = 0; + + // delete page directory + switch_pagedir(get_kernel_pagedir()); + delete_pagedir(p->pd); + p->pd = 0; + + // orphan children + while (p->children != 0) { + process_t *c = p->children; + p->children = c->next_child; + + c->parent = 0; + c->next_child = 0; + } + + if (p->parent == 0) { + free(p); + } else { + // TODO : notify parent + } + + exit_critical(st); +} + +void process_thread_deleted(thread_t *t) { + int st = enter_critical(CL_NOSWITCH); + + process_t *p = t->proc; + if (p->threads == t) { + p->threads = t->next_in_proc; + } else { + for (thread_t *it = p->threads; it != 0; it = it->next_in_proc) { + if (it->next_in_proc == t) { + it->next_in_proc = t->next_in_proc; + break; + } + } + } + + if (p->threads == 0 && p->status == PS_RUNNING) + process_exit(p, PS_FINISHED, 0); + + exit_critical(st); +} + // ================================== // // MANAGING FILESYSTEMS FOR PROCESSES // @@ -344,7 +456,7 @@ bool munmap(process_t *proc, void* addr) { static void proc_user_exception(registers_t *regs) { dbg_printf("Usermode exception in user process : exiting.\n"); dbg_dump_registers(regs); - exit(); + process_exit(current_process(), PS_FAILURE, FAIL_EXCEPTION); } static void proc_usermem_pf(void* p, registers_t *regs, void* addr) { process_t *proc = (process_t*)p; @@ -353,13 +465,13 @@ static void proc_usermem_pf(void* p, registers_t *regs, void* addr) { if (r == 0) { dbg_printf("Segmentation fault in process %d (0x%p : not mapped) : exiting.\n", proc->pid, addr); dbg_dump_registers(regs); - exit(); + process_exit(current_process(), PS_FAILURE, (addr < (void*)PAGE_SIZE ? FAIL_ZEROPTR : FAIL_SEGFAULT)); } bool wr = ((regs->err_code & PF_WRITE_BIT) != 0); if (wr && !(r->mode & MM_WRITE)) { dbg_printf("Segmentation fault in process %d (0x%p : not allowed to write) : exiting.\n", proc->pid, addr); - exit(); + process_exit(current_process(), PS_FAILURE, (addr < (void*)PAGE_SIZE ? FAIL_ZEROPTR : FAIL_SEGFAULT)); } bool pr = ((regs->err_code & PF_PRESENT_BIT) != 0); @@ -393,7 +505,7 @@ void probe_for_read(const void* addr, size_t len) { if (r == 0 || addr + len > r->addr + r->size || !(r->mode & MM_READ)) { dbg_printf("Access violation on read at 0x%p len 0x%p in process %d : exiting.\n", addr, len, proc->pid); - exit(); + process_exit(current_process(), PS_FAILURE, FAIL_SC_SEGFAULT); } } @@ -403,7 +515,7 @@ void probe_for_write(const void* addr, size_t len) { if (r == 0 || addr + len > r->addr + r->size || !(r->mode & MM_WRITE)) { dbg_printf("Access violation on write at 0x%p len 0x%p in process %d : exiting.\n", addr, len, proc->pid); - exit(); + process_exit(current_process(), PS_FAILURE, FAIL_SC_SEGFAULT); } } diff --git a/src/kernel/user/syscall.c b/src/kernel/user/syscall.c index 009903c..d9131b2 100644 --- a/src/kernel/user/syscall.c +++ b/src/kernel/user/syscall.c @@ -33,9 +33,9 @@ static char* sc_copy_string(uint32_t s, uint32_t slen) { static uint32_t exit_sc(sc_args_t args) { dbg_printf("Proc %d exit with code %d\n", current_process()->pid, args.a); - // TODO : use code... and process exiting is not supposed to be done this way - exit(); + process_exit(current_process(), PS_FINISHED, args.a); + ASSERT(false); return 0; } |