diff --git a/kernel/inc/proc/process.h b/kernel/inc/proc/process.h new file mode 100644 index 0000000..cdf3b10 --- /dev/null +++ b/kernel/inc/proc/process.h @@ -0,0 +1,51 @@ +/* + * Copyright 2023 Antifallobst + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the “Software”), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef NOX_PROCESS_H +#define NOX_PROCESS_H + +#include "utils/stdtypes.h" +#include "utils/string.h" + +typedef uint32_t pid_t; + +typedef enum { + PROCESS_NONE, + PROCESS_KERNEL +} processes_standard_E; + +typedef struct process_T process_T; +struct process_T{ + char name[128]; + pid_t id; + void* chunk; + uint32_t chunk_id; + + process_T* parent; + void* threads; +}; + +pid_t process_spawn (pid_t parent, string_t name); +void process_kill (pid_t process); + +#endif //NOX_PROCESS_H diff --git a/kernel/inc/proc/scheduler.h b/kernel/inc/proc/scheduler.h index 90698ec..928f812 100644 --- a/kernel/inc/proc/scheduler.h +++ b/kernel/inc/proc/scheduler.h @@ -25,27 +25,48 @@ #define NOX_SCHEDULER_H #include "proc/thread.h" +#include "proc/process.h" #include "utils/bitmap.h" -typedef struct { - uint32_t num_threads; - thread_T* running_thread; +#define SCHEDULER_PROCESS_CHUNK_SIZE 64 - bool blocked; - bool initialized; +typedef struct scheduler_processes_chunk_T scheduler_processes_chunk_T; +struct scheduler_processes_chunk_T { + process_T** processes; + bitmap_T processes_bitmap; + + uint32_t num_free_pids; + + scheduler_processes_chunk_T* prev; + scheduler_processes_chunk_T* next; +}; + +typedef struct { + uint32_t num_threads; + uint32_t num_processes; + thread_T* running_thread; + scheduler_processes_chunk_T* processes; + + bool blocked; + bool initialized; } scheduler_T; -void scheduler_init (); -cpu_state_T* scheduler_start (cpu_state_T* state); -bool scheduler_is_initialized (); +void scheduler_init (); +cpu_state_T* scheduler_start (cpu_state_T* state); +bool scheduler_is_initialized (); -thread_T* scheduler_register_thread (thread_T* thread); -void scheduler_pause_thread (thread_T* thread); -void scheduler_start_thread (thread_T* thread); -void scheduler_kill_thread (thread_T* thread); +thread_T* scheduler_register_thread (thread_T* thread); +void scheduler_pause_thread (thread_T* thread); +void scheduler_start_thread (thread_T* thread); +void scheduler_kill_thread (thread_T* thread); -thread_T* scheduler_get_current_thread (); +pid_t scheduler_register_process (process_T* process); +void scheduler_kill_process (process_T* process); +process_T* scheduler_get_process (pid_t pid); -cpu_state_T* scheduler_switch_context (cpu_state_T* state); +thread_T* scheduler_get_current_thread (); +process_T* scheduler_get_current_process (); + +cpu_state_T* scheduler_switch_context (cpu_state_T* state); #endif //NOX_SCHEDULER_H diff --git a/kernel/inc/proc/thread.h b/kernel/inc/proc/thread.h index 8e4425f..0a15006 100644 --- a/kernel/inc/proc/thread.h +++ b/kernel/inc/proc/thread.h @@ -26,6 +26,7 @@ #include "utils/stdtypes.h" #include "platform/cpu.h" +#include "proc/process.h" typedef struct thread_T thread_T; struct thread_T{ @@ -33,15 +34,19 @@ struct thread_T{ uint64_t cpu_time; void* stack; uint32_t stack_size; + process_T* process; // Scheduling data - thread_T* prev; - thread_T* next; + thread_T* global_prev; + thread_T* global_next; + + thread_T* local_prev; + thread_T* local_next; }; -thread_T* thread_spawn (void* function); -thread_T* thread_spawn_from_state (cpu_state_T* state); +thread_T* thread_spawn (pid_t process, void* function); +thread_T* thread_spawn_from_state (pid_t process, cpu_state_T* state); void thread_start (thread_T* thread); void thread_pause (thread_T* thread); void thread_kill (thread_T* thread); diff --git a/kernel/src/kmain.c b/kernel/src/kmain.c index d768a8a..bf3ba15 100644 --- a/kernel/src/kmain.c +++ b/kernel/src/kmain.c @@ -78,6 +78,26 @@ void kernel_init(boot_info_T* boot_info) { limine_terminal_print(boot_info, " ok\n"); } +#include "utils/io.h" + +void test1() { + while (true) { + io_out_byte(LOG_PORT, 'A'); + } +} + +void test2() { + while (true) { + io_out_byte(LOG_PORT, 'B'); + } +} + +void test3() { + while (true) { + io_out_byte(LOG_PORT, 'C'); + } +} + void kmain(boot_info_T boot_info) { limine_terminal_print(&boot_info, "Booting NoxOS...\n"); @@ -90,16 +110,29 @@ void kmain(boot_info_T boot_info) { elf_init_kernel_exec(&boot_info); - vfs_node_T* node = vfs_resolve_path(&g_root_fs, "/initrd/test.elf"); + pid_t proc1 = process_spawn(PROCESS_NONE, "test process 1"); + thread_T* proc1_thread1 = thread_spawn(proc1, test1); + thread_T* proc1_thread2 = thread_spawn(proc1, test2); + thread_T* proc1_thread3 = thread_spawn(proc1, test3); - elf_executable_T* exec = elf_executable_create(node->cache->buffer); + thread_start(proc1_thread1); + thread_start(proc1_thread2); + thread_start(proc1_thread3); - page_map_T* page_map = pframe_request(); - elf_mappings_apply(exec->mappings, exec->num_mappings, node->cache->buffer, 0xFFFFFFFFFF000000, page_map); + process_kill(proc1); - int (*entry)() = (0xFFFFFFFFFF000000 + symbol_resolve_from_name(exec->symbols, exec->num_symbols, "_start")->address); +// vfs_node_T* node = vfs_resolve_path(&g_root_fs, "/initrd/test.elf"); +// +// elf_executable_T* exec = elf_executable_create(node->cache->buffer); +// +// page_map_T* page_map = pframe_request(); +// elf_mappings_apply(exec->mappings, exec->num_mappings, node->cache->buffer, 0xFFFFFFFFFF000000, page_map); +// +// int (*entry)() = (0xFFFFFFFFFF000000 + symbol_resolve_from_name(exec->symbols, exec->num_symbols, "_start")->address); +// +// thread_spawn(PROCESS_KERNEL, entry); - log(LOG_DEBUG, "ELF returned: %d", entry()); +// log(LOG_DEBUG, "ELF returned: %d", entry()); CORE_HALT_FOREVER } diff --git a/kernel/src/proc/process.c b/kernel/src/proc/process.c new file mode 100644 index 0000000..bdf0865 --- /dev/null +++ b/kernel/src/proc/process.c @@ -0,0 +1,47 @@ +/* + * Copyright 2023 Antifallobst + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the “Software”), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "proc/process.h" +#include "proc/scheduler.h" +#include "utils/memory.h" +#include "utils/math.h" + +pid_t process_spawn(pid_t parent, string_t name) { + process_T* process = memory_allocate(sizeof(process_T)); + process->threads = NULL; + process->parent = scheduler_get_process(parent); + + memory_copy(name, process->name, MIN(string_length(name), 127)); + + // the processes' id, chunk_id and chunk fields will be set by the scheduler + + return scheduler_register_process(process); +} + +void process_kill(pid_t process) { + process_T* proc = scheduler_get_process(process); + + scheduler_kill_process(proc); + + memory_free(proc); +} \ No newline at end of file diff --git a/kernel/src/proc/scheduler.c b/kernel/src/proc/scheduler.c index e4d535d..0210214 100644 --- a/kernel/src/proc/scheduler.c +++ b/kernel/src/proc/scheduler.c @@ -25,22 +25,52 @@ #include "utils/memory.h" #include "utils/logger.h" #include "utils/core.h" +#include "drivers/graphics/renderer.h" #include "platform/syscall.h" -scheduler_T g_scheduler; +scheduler_T g_scheduler; +graphics_buffer_T* g_scheduler_info_buffer; + +void scheduler_update_info() { + uint64_t size = format_size("Processes[%d]\n Threads[%d]", g_scheduler.num_processes, g_scheduler.num_threads); + char buffer[size]; + format(buffer, "Processes[%d]\n Threads[%d]", g_scheduler.num_processes, g_scheduler.num_threads); + + graphics_buffer_draw_string(g_scheduler_info_buffer, 0, 0, g_color_palette[COLOR_PAL_GREEN], buffer); +} + +scheduler_processes_chunk_T* scheduler_processes_chunk_create(scheduler_processes_chunk_T* prev) { + scheduler_processes_chunk_T* chunk = memory_allocate(sizeof(scheduler_processes_chunk_T)); + + chunk->processes = memory_allocate(SCHEDULER_PROCESS_CHUNK_SIZE * sizeof(process_T*)); + chunk->prev = prev; + chunk->next = NULL; + chunk->processes_bitmap = bitmap_init(SCHEDULER_PROCESS_CHUNK_SIZE); + chunk->num_free_pids = SCHEDULER_PROCESS_CHUNK_SIZE; + + return chunk; +} void scheduler_init() { - g_scheduler.num_threads = 0; - g_scheduler.running_thread = NULL; - g_scheduler.blocked = false; + g_scheduler.num_threads = 0; + g_scheduler.num_processes = 0; + g_scheduler.running_thread = NULL; + g_scheduler.blocked = false; + + g_scheduler.processes = scheduler_processes_chunk_create(NULL); + + g_scheduler_info_buffer = graphics_buffer_request(graphics_renderer_get_width()-100, 0, 100, 200, GRAPHICS_BUFFER_STANDARD); syscall_perform(SYSCALL_KERNEL_SCHEDULER_START); } cpu_state_T* scheduler_start(cpu_state_T* state) { - thread_T* thread = thread_spawn_from_state(state); - thread->prev = thread; - thread->next = thread; + + process_spawn(PROCESS_NONE, "kernel"); + + thread_T* thread = thread_spawn_from_state(PROCESS_KERNEL, state); + thread->global_prev = thread; + thread->global_next = thread; g_scheduler.running_thread = thread; @@ -58,25 +88,36 @@ bool scheduler_is_initialized() { void scheduler_queue_add_thread(thread_T* thread) { if (g_scheduler.running_thread == NULL) { return; } - thread->prev = g_scheduler.running_thread; - thread->next = g_scheduler.running_thread->next; - g_scheduler.running_thread->next->prev = thread; - g_scheduler.running_thread->next = thread; + thread->global_prev = g_scheduler.running_thread; + thread->global_next = g_scheduler.running_thread->global_next; + g_scheduler.running_thread->global_next->global_prev = thread; + g_scheduler.running_thread->global_next = thread; } void scheduler_queue_remove_thread(thread_T* thread) { - if (thread->prev == NULL || thread->next == NULL) { return; } + if (thread->global_prev == NULL || thread->global_next == NULL) { return; } - thread->prev->next = thread->next; - thread->next->prev = thread->prev; - thread->prev = NULL; - thread->next = NULL; + thread->global_prev->global_next = thread->global_next; + thread->global_next->global_prev = thread->global_prev; + thread->global_prev = NULL; + thread->global_next = NULL; } thread_T* scheduler_register_thread(thread_T* thread) { + thread->local_prev = NULL; + thread->local_next = NULL; + + if (thread->process->threads != NULL) { + thread->local_next = thread->process->threads; + thread->local_next->local_prev = thread; + } + + thread->process->threads = thread; + g_scheduler.num_threads++; log(LOG_INFO, " Registered thread"); + scheduler_update_info(); return thread; } @@ -84,25 +125,129 @@ thread_T* scheduler_register_thread(thread_T* thread) { void scheduler_pause_thread(thread_T* thread) { scheduler_queue_remove_thread(thread); log(LOG_INFO, " Paused thread"); + scheduler_update_info(); } void scheduler_start_thread(thread_T* thread) { scheduler_queue_add_thread(thread); log(LOG_INFO, " Started thread"); + scheduler_update_info(); } void scheduler_kill_thread(thread_T* thread) { scheduler_queue_remove_thread(thread); + if (thread->process->threads == thread) { + thread->process->threads = thread->local_next; + } + + if (thread->local_next != NULL) { + thread->local_next->local_prev = thread->local_prev; + } + + if (thread->local_prev != NULL) { + thread->local_prev->local_next = thread->local_next; + } + g_scheduler.num_threads--; log(LOG_INFO, " Killed thread"); + scheduler_update_info(); +} + +pid_t scheduler_register_process(process_T* process) { + pid_t pid = 1; // offset pit by one, so this won't return a pid that is PROCESS_NONE + + // find a process chunk, that has free slots + scheduler_processes_chunk_T* chunk = g_scheduler.processes; + while (chunk->num_free_pids == 0) { + if (chunk->next == NULL) { + chunk->next = scheduler_processes_chunk_create(chunk); + } + chunk = chunk->next; + pid += SCHEDULER_PROCESS_CHUNK_SIZE; + } + + for (uint32_t i = 0; i < SCHEDULER_PROCESS_CHUNK_SIZE; i++) { + if (bitmap_get(&chunk->processes_bitmap, i)) { + continue; + } + + bitmap_set(&chunk->processes_bitmap, i, true); + chunk->processes[i] = process; + process->chunk = chunk; + process->chunk_id = i; + process->id = pid + i; + + chunk->num_free_pids--; + g_scheduler.num_processes++; + + log(LOG_INFO, " Registered Process %d", pid + i); + scheduler_update_info(); + + return pid + i; + } + + // if this happens, something went really wrong + log(LOG_ERROR, " failed to register process (no free slots in chunk)"); + return PROCESS_NONE; +} + +void scheduler_kill_process(process_T* process) { + scheduler_processes_chunk_T* chunk = process->chunk; + + bitmap_set(&chunk->processes_bitmap, process->chunk_id, false); + chunk->processes[process->chunk_id] = NULL; + + + thread_T* thread = process->threads; + while (thread != NULL) { + thread_T* next = thread->local_next; + thread_kill(thread); + thread = next; + } + + chunk->num_free_pids++; + g_scheduler.num_processes--; + + log(LOG_INFO, " Killed Process %d", process->id); + scheduler_update_info(); +} + +process_T* scheduler_get_process(pid_t pid) { + if (pid == PROCESS_NONE) { + return NULL; + } + + pid--; // apply pid offset to make PROCESS_NONE work + + uint32_t num_chunks = pid / SCHEDULER_PROCESS_CHUNK_SIZE; + uint32_t index_in_chunk = pid % SCHEDULER_PROCESS_CHUNK_SIZE; + + scheduler_processes_chunk_T* chunk = g_scheduler.processes; + for (uint32_t i = 0; i < num_chunks; i++) { + chunk = chunk->next; + + if (chunk == NULL) { + return NULL; + } + } + + if (!bitmap_get(&chunk->processes_bitmap, index_in_chunk)) { + return NULL; + } + + return chunk->processes[index_in_chunk]; } thread_T* scheduler_get_current_thread() { return g_scheduler.running_thread; } +process_T* scheduler_get_current_process() { + return g_scheduler.running_thread->process; +} + cpu_state_T* scheduler_switch_context(cpu_state_T* state) { if (!g_scheduler.initialized) { return state; @@ -112,14 +257,14 @@ cpu_state_T* scheduler_switch_context(cpu_state_T* state) { g_scheduler.blocked = true; thread_T* old_thread = g_scheduler.running_thread; - thread_T* new_thread = g_scheduler.running_thread->next; + thread_T* new_thread = g_scheduler.running_thread->global_next; if (old_thread->cpu_time > 0) { memory_copy(state, &old_thread->state, sizeof(cpu_state_T)); } old_thread->cpu_time += 1; - g_scheduler.running_thread = g_scheduler.running_thread->next; + g_scheduler.running_thread = g_scheduler.running_thread->global_next; g_scheduler.blocked = false; return &new_thread->state; diff --git a/kernel/src/proc/thread.c b/kernel/src/proc/thread.c index a8380a1..e04cf00 100644 --- a/kernel/src/proc/thread.c +++ b/kernel/src/proc/thread.c @@ -27,10 +27,17 @@ #include "mm/page_frame.h" #include "platform/gdt.h" #include "utils/memory.h" +#include "utils/logger.h" -thread_T* thread_spawn(void* function) { +thread_T* thread_spawn(pid_t process, void* function) { thread_T* thread = memory_allocate(sizeof(thread_T)); + thread->process = scheduler_get_process(process); + if (thread->process == NULL) { + log(LOG_WARNING, " failed to spawn thread for process %d (invalid pid)", process); + return NULL; + } + thread->stack_size = PFRAME_SIZE * 4; thread->stack = memory_allocate(thread->stack_size); thread->cpu_time = 0; @@ -56,9 +63,15 @@ thread_T* thread_spawn(void* function) { return scheduler_register_thread(thread); } -thread_T* thread_spawn_from_state(cpu_state_T* state) { +thread_T* thread_spawn_from_state(pid_t process, cpu_state_T* state) { thread_T* thread = memory_allocate(sizeof(thread_T)); + thread->process = scheduler_get_process(process); + if (thread->process == NULL) { + log(LOG_WARNING, " failed to spawn thread for process %d (invalid pid)", process); + return NULL; + } + thread->stack_size = 0; thread->stack = NULL; thread->cpu_time = 0;