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.
master
Anton Lydike 3 years ago
parent 1a6abb5e1b
commit df0a944528

@ -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();

@ -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");
}

@ -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

@ -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);
}

@ -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);

@ -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;
}

@ -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

Loading…
Cancel
Save