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.
This commit is contained in:
parent
1a6abb5e1b
commit
df0a944528
3
kernel.h
3
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();
|
||||
|
||||
|
@ -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…
Reference in New Issue
Block a user