From df0a944528becade68806a4d6bdace69fbccd9b8 Mon Sep 17 00:00:00 2001 From: Anton Lydike Date: Mon, 23 Aug 2021 09:35:00 +0200 Subject: [PATCH] Reuse stacks of dead processes In order to make the stack-space of dead processes usable again, I addeed a stack data structure which holds unused stack pointers. When a process is killed, the destructor is called, which will free the stack associated with the process. --- kernel.h | 3 +++ kinclude/ecall.c | 26 +++++++++++++++++++++++--- kinclude/ktypes.h | 41 +++++++++++++++++++++++++++++++++++++++++ kinclude/malloc.c | 42 +++++++++++++++++++++++++++++++++++++----- kinclude/malloc.h | 3 ++- kinclude/sched.c | 15 ++++++++++++++- kinclude/sched.h | 1 + 7 files changed, 121 insertions(+), 10 deletions(-) diff --git a/kernel.h b/kernel.h index 6de9112..f336a57 100644 --- a/kernel.h +++ b/kernel.h @@ -10,6 +10,9 @@ // scheduler settings #define TIME_SLICE_LEN 10 // number of cpu time ticks per slice +// set size of allocated stack for user processes +#define USER_STACK_SIZE (1 << 12) + // init function extern __attribute__((__noreturn__)) void init(); diff --git a/kinclude/ecall.c b/kinclude/ecall.c index a49e089..e54d478 100644 --- a/kinclude/ecall.c +++ b/kinclude/ecall.c @@ -73,7 +73,26 @@ optional_int ecall_handle_join(int* args, ProcessControlBlock* pcb) optional_int ecall_handle_kill(int* args, ProcessControlBlock* pcb) { - return (optional_int) { .error = EINVAL }; + int pid = args[0]; + ProcessControlBlock* target = process_from_pid(pid); + + // return error if no process has that id + if (target == NULL) + return (optional_int) { .error = ESRCH }; + + // return success if the process is dead + if (target == PROC_DEAD) + return (optional_int) { .value = 1 }; + + // kill target by marking it dead + target->status = PROC_DEAD; + target->exit_code = -10; // set unique exit code + + // call process destructor + destroy_process(pcb); + + // return success + return (optional_int) { .value = 1 }; } optional_int ecall_handle_exit(int* args, ProcessControlBlock* pcb) @@ -92,8 +111,8 @@ optional_int ecall_handle_exit(int* args, ProcessControlBlock* pcb) dbgln(msg, 34); } - // recursively kill all child processes - kill_child_processes(pcb); + // call process destructor + destroy_process(pcb); return (optional_int) { .value = 0 }; } @@ -183,6 +202,7 @@ void init_ecall_table() void handle_exception(int ecode, int mtval) { + //TODO: handle exceptions well dbgln("exception encountered!", 17); __asm__ ("ebreak"); } diff --git a/kinclude/ktypes.h b/kinclude/ktypes.h index 10835eb..1ac5e0b 100644 --- a/kinclude/ktypes.h +++ b/kinclude/ktypes.h @@ -42,6 +42,7 @@ struct ProcessControlBlock { unsigned long long int asleep_until; // parent ProcessControlBlock* parent; + void* stack_top; }; enum pcb_struct_registers { @@ -88,6 +89,8 @@ typedef struct loaded_binary { type value; \ } optional_ ## type +// has_value and has_error are not dependent on the value type +// therefore we can define them as macros #define has_value(optional) (optional.error == 0) #define has_error(optional) (!has_value(optional)) @@ -104,4 +107,42 @@ CreateOptionalOfType(size_t); CreateOptionalOfType(pcbptr); CreateOptionalOfType(voidptr); +/* + * Stacks + * + * stacks allow pushing and popping elements off it. + */ + +// we currently only need a voidptr stack, if we need more we can either move them to a separate file +// or just type cast the pointers afterwards +struct voidptr_stack { + void** data; + int pos; + int size; +}; + +inline void* voidptr_stack_pop(struct voidptr_stack* stack) +{ + if (stack->pos == -1) + return NULL; + return stack->data[stack->pos--]; +} + +inline int voidptr_stack_push(struct voidptr_stack* stack, void* ptr) +{ + if (stack->pos + 1 > stack->size) + return 0; + + stack->pos += 1; + stack->data[stack->pos] = ptr; + return 1; +} + +inline void voidptr_stack_new(struct voidptr_stack* stack, void** data, int size) +{ + stack->pos = -1; + stack->size = size; + stack->data = data; +} + #endif diff --git a/kinclude/malloc.c b/kinclude/malloc.c index d38a3ac..89f1997 100644 --- a/kinclude/malloc.c +++ b/kinclude/malloc.c @@ -1,26 +1,58 @@ +#include "../kernel.h" #include "malloc.h" #include "ecall.h" +#include "io.h" -malloc_info global_malloc_info = { 0 }; -void* allocate_memory_end; +// information about the systems memory layout is stored here +static malloc_info global_malloc_info = { 0 }; +// this pointer points to the end of unused memory +static void* allocate_memory_end; +// this stack holds currently unused program stacks +// it is a stack in the sense that you can push and pop variables +static struct voidptr_stack unused_stacks; +void stash_stack(void* stack_top) +{ + if (voidptr_stack_push(&unused_stacks, stack_top) == 0) { + // if this happens, we know we messed up and created more stacks than we + // could ever have processes running simultaneously! + // we probably have an erro in retrieving stashed stacks? + dbgln("cannot stash stack, no space left! This should never ever happen!", 65); + } +} void malloc_init(malloc_info* given_info) { global_malloc_info = *given_info; allocate_memory_end = given_info->allocate_memory_end; + allocate_memory_end = (void*) (((int) allocate_memory_end) - PROCESS_COUNT); + voidptr_stack_new(&unused_stacks, (void**) allocate_memory_end, PROCESS_COUNT); } // allocate stack and return a pointer to the *end* of the allocated region // this doesn't reuse stack from functions which exited -optional_voidptr malloc_stack(size_t size) +optional_voidptr malloc_stack() { - void* new_alloc_end = (void*) (((int) allocate_memory_end) - size); + // try to reuse a stack top + void* stack_top = voidptr_stack_pop(&unused_stacks); + + if (stack_top != NULL) { + return (optional_voidptr) { .value = stack_top }; + } + + // otherwise allocate a new stack at the end of available memory + void* new_alloc_end = (void*) (((int) allocate_memory_end) - USER_STACK_SIZE); + // check if we ran out of space if (new_alloc_end < global_malloc_info.allocate_memory_start) return (optional_voidptr) { .error = ENOMEM }; - void* stack_top = allocate_memory_end; + stack_top = allocate_memory_end; allocate_memory_end = new_alloc_end; return (optional_voidptr) { .value = stack_top }; } + +void free_stack(void* stack_top) +{ + stash_stack(stack_top); +} diff --git a/kinclude/malloc.h b/kinclude/malloc.h index ee1d650..c48bf85 100644 --- a/kinclude/malloc.h +++ b/kinclude/malloc.h @@ -8,7 +8,8 @@ typedef struct malloc_info { void* allocate_memory_start; } malloc_info; -optional_voidptr malloc_stack(size_t size); +optional_voidptr malloc_stack(); +void free_stack(void* stack_top); void malloc_init(malloc_info* info); diff --git a/kinclude/sched.c b/kinclude/sched.c index 7bde96c..7ccf9e4 100644 --- a/kinclude/sched.c +++ b/kinclude/sched.c @@ -229,6 +229,7 @@ optional_pcbptr create_new_process(loaded_binary* bin, int stack_size) pcb->binary = bin; pcb->parent = NULL; pcb->asleep_until = 0; + pcb->stack_top = stack_top_or_err.value; // zero out registers memset(0, pcb->regs, pcb->regs + 31); // load stack top into stack pointer register @@ -273,6 +274,7 @@ optional_pcbptr create_new_thread(ProcessControlBlock* parent, void* entrypoint, pcb->binary = parent->binary; pcb->parent = parent; pcb->asleep_until = 0; + pcb->stack_top = stack_top_or_err.value; // zero out registers memset(0, pcb->regs, pcb->regs + 31); // set return address to global thread finalizer @@ -293,7 +295,8 @@ void kill_child_processes(ProcessControlBlock* pcb) { for (int i = 0; i < PROCESS_COUNT; i++) { ProcessControlBlock* proc = processes + i; - if (proc->parent != pcb) + // if this is not a child process or already exited + if (proc->parent != pcb || proc->status == PROC_DEAD) continue; proc->status = PROC_DEAD; @@ -301,3 +304,13 @@ void kill_child_processes(ProcessControlBlock* pcb) kill_child_processes(proc); } } + +void destroy_process(ProcessControlBlock* pcb) +{ + // kill child processes + kill_child_processes(pcb); + // free allocated stack + free_stack(pcb->stack_top); + // make sure the thread is not rescheduled + pcb->status = PROC_DEAD; +} diff --git a/kinclude/sched.h b/kinclude/sched.h index 03f0ed6..16ed363 100644 --- a/kinclude/sched.h +++ b/kinclude/sched.h @@ -21,5 +21,6 @@ void mark_ecall_entry(); // process creation / destruction optional_pcbptr create_new_process(loaded_binary*, int); optional_pcbptr create_new_thread(ProcessControlBlock*, void*, void*, int); +void destroy_process(ProcessControlBlock* pcb); void kill_child_processes(ProcessControlBlock* pcb); #endif