From f550660e21f752217f78382d09f92545e256e1e8 Mon Sep 17 00:00:00 2001 From: antifallobst Date: Sun, 19 Feb 2023 00:05:45 +0100 Subject: [PATCH] fix (kernel): rewrote the complete scheduler so, that it has no max_threads limit and is faster --- kernel/inc/proc/scheduler.h | 30 +++----- kernel/inc/proc/thread.h | 23 +++++- kernel/src/kmain.c | 32 +++----- kernel/src/platform/interrupts.c | 1 - kernel/src/proc/scheduler.c | 124 ++++++++++--------------------- kernel/src/proc/thread.c | 81 ++++++++++++++++++++ 6 files changed, 160 insertions(+), 131 deletions(-) create mode 100644 kernel/src/proc/thread.c diff --git a/kernel/inc/proc/scheduler.h b/kernel/inc/proc/scheduler.h index 0557643..9d688e9 100644 --- a/kernel/inc/proc/scheduler.h +++ b/kernel/inc/proc/scheduler.h @@ -23,36 +23,24 @@ #define SCHEDULER_MAX_THREADS_PER_PROCESS 16 typedef struct { - uint32_t max_threads; - uint32_t num_threads; + uint32_t num_threads; + thread_descriptor_T* running_thread; - thread_T* threads; - - bitmap_T threads_bitmap; - bitmap_T running_threads_bitmap; - - thread_T** queue; - uint32_t queue_index; - uint32_t queue_length; - uint32_t running_thread; - - bool blocked; - bool initialized; + bool blocked; + bool initialized; } scheduler_T; void scheduler_init (); cpu_state_T* scheduler_start (cpu_state_T* state); bool scheduler_is_initialized (); -uint32_t scheduler_register_thread (thread_T* thread); -void scheduler_pause_thread (uint32_t id); -void scheduler_start_thread (uint32_t id); -void scheduler_kill_thread (uint32_t id); -thread_T* scheduler_get_thread (uint32_t id); +thread_t scheduler_register_thread (thread_T* thread); +void scheduler_pause_thread (thread_t thread_descriptor); +void scheduler_start_thread (thread_t thread_descriptor); +void scheduler_kill_thread (thread_t thread_descriptor); -uint32_t scheduler_get_current_thread (); +thread_t scheduler_get_current_thread (); -void scheduler_calculate_queue (); 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 3090312..46879aa 100644 --- a/kernel/inc/proc/thread.h +++ b/kernel/inc/proc/thread.h @@ -23,10 +23,27 @@ typedef enum { THREAD_NONE = -1 } thread_standard_E; +typedef struct thread_descriptor_T thread_descriptor_T; +typedef const thread_descriptor_T* thread_t; + typedef struct { - uint32_t id; - cpu_state_T state; - uint64_t cpu_time; + cpu_state_T state; + uint64_t cpu_time; + void* stack; + uint32_t stack_size; + thread_descriptor_T* descriptor; } thread_T; +struct thread_descriptor_T{ + thread_descriptor_T* prev; + thread_descriptor_T* next; + thread_T* thread; +}; + +thread_t thread_spawn (void* function); +thread_t thread_spawn_from_state (cpu_state_T* state); +void thread_start (thread_t thread_descriptor); +void thread_pause (thread_t thread_descriptor); +void thread_kill (thread_t thread_descriptor); + #endif //NOX_THREAD_H diff --git a/kernel/src/kmain.c b/kernel/src/kmain.c index be3a401..15b3187 100644 --- a/kernel/src/kmain.c +++ b/kernel/src/kmain.c @@ -56,6 +56,12 @@ void test_b() { } } +void test_c() { + while (true) { + io_out_byte(LOG_PORT, 'C'); + } +} + void kmain(boot_info_T boot_info) { limine_terminal_print(&boot_info, "Booting NoxOS...\n"); @@ -66,27 +72,11 @@ void kmain(boot_info_T boot_info) { limine_terminal_print(&boot_info, "Kernel initialized\n"); log(LOG_INFO, "!=====[ Kernel Initialized ]=====!\n"); - void* stack = memory_allocate(PFRAME_SIZE * 10); - thread_T test; - test.state = (cpu_state_T){ - .cr3 = (uint64_t)g_kernel_page_map, - .rax = 0, - .rbx = 0, - .rcx = 0, - .rdx = 0, - .rsi = 0, - .rdi = 0, - .rbp = (uint64_t)stack + (PFRAME_SIZE * 10), - .interrupt_id = 0, - .error_code = 0, - .rip = (uint64_t)test_b, - .cs = GDT_SELECTOR_KERNEL_CODE, - .flags = 1 << CPU_FLAG_INTERRUPT_ENABLE, - .rsp = (uint64_t)stack + (PFRAME_SIZE * 10), - .ss = GDT_SELECTOR_KERNEL_DATA - }; - scheduler_register_thread(&test); - scheduler_start_thread(test.id); + thread_t thread = thread_spawn(test_b); + thread_start(thread); + + thread_t thread1 = thread_spawn(test_c); + thread_start(thread1); test_a(); diff --git a/kernel/src/platform/interrupts.c b/kernel/src/platform/interrupts.c index d76279a..a6a85ab 100644 --- a/kernel/src/platform/interrupts.c +++ b/kernel/src/platform/interrupts.c @@ -248,7 +248,6 @@ cpu_state_T* irq_handle(cpu_state_T* state, pic_irq_E irq) { } cpu_state_T* interrupts_handle(cpu_state_T* state) { - log(LOG_DEBUG, "INT 0x%xb", state->interrupt_id); if (state->interrupt_id < EXCEPTIONS_ENUM_END) { return exception_handle(state); } diff --git a/kernel/src/proc/scheduler.c b/kernel/src/proc/scheduler.c index f954d7e..9f62285 100644 --- a/kernel/src/proc/scheduler.c +++ b/kernel/src/proc/scheduler.c @@ -22,29 +22,21 @@ scheduler_T g_scheduler; void scheduler_init() { - g_scheduler.max_threads = SCHEDULER_MAX_THREADS_PER_PROCESS * SCHEDULER_MAX_PROCESSES; g_scheduler.num_threads = 0; - g_scheduler.threads = memory_allocate(g_scheduler.max_threads * sizeof(thread_T)); - g_scheduler.threads_bitmap = bitmap_init(g_scheduler.max_threads); - g_scheduler.running_threads_bitmap = bitmap_init(g_scheduler.max_threads); - g_scheduler.queue = memory_allocate(g_scheduler.max_threads * sizeof(thread_T*)); - g_scheduler.queue_index = 0; - g_scheduler.queue_length = 0; - g_scheduler.running_thread = 0; + g_scheduler.running_thread = NULL; g_scheduler.blocked = false; syscall_perform(SYSCALL_KERNEL_SCHEDULER_START); } cpu_state_T* scheduler_start(cpu_state_T* state) { - thread_T thread; + thread_descriptor_T* thread = thread_spawn_from_state(state); + thread->prev = thread; + thread->next = thread; + g_scheduler.running_thread = thread; - memory_copy(state, &thread.state, sizeof(cpu_state_T)); - thread.id = 0; - thread.cpu_time = 0; - scheduler_register_thread(&thread); - scheduler_start_thread(thread.id); + scheduler_start_thread(thread); g_scheduler.initialized = true; @@ -55,113 +47,75 @@ bool scheduler_is_initialized() { return g_scheduler.initialized; } -uint32_t scheduler_request_thread_id() { - for (int i = 0; i < g_scheduler.max_threads; i++) { - if (!bitmap_get(&g_scheduler.threads_bitmap, i)) { - return i; - } - } - return THREAD_NONE; +void scheduler_queue_add_thread_descriptor(thread_descriptor_T* descriptor) { + if (g_scheduler.running_thread == NULL) { return; } + + descriptor->prev = g_scheduler.running_thread; + descriptor->next = g_scheduler.running_thread->next; + g_scheduler.running_thread->next->prev = descriptor; + g_scheduler.running_thread->next = descriptor; } -uint32_t scheduler_register_thread(thread_T* thread) { - if (g_scheduler.num_threads >= g_scheduler.max_threads) { - log(LOG_WARNING, " Failed to register Thread (max thread num reached)"); - return THREAD_NONE; - } +void scheduler_queue_remove_thread_descriptor(thread_descriptor_T* descriptor) { + if (descriptor->prev == NULL || descriptor->next == NULL) { return; } + descriptor->prev->next = descriptor->next; + descriptor->next->prev = descriptor->prev; + descriptor->prev = NULL; + descriptor->next = NULL; +} - thread->id = scheduler_request_thread_id(); - if (thread->id == THREAD_NONE) { - log(LOG_WARNING, " Failed to register Thread (generation of thread id failed)"); - return THREAD_NONE; - } - - bitmap_set(&g_scheduler.threads_bitmap, thread->id, true); - memory_copy(thread, &g_scheduler.threads[thread->id], sizeof(thread_T)); +thread_t scheduler_register_thread(thread_T* thread) { + thread->descriptor = memory_allocate(sizeof(thread_descriptor_T)); + thread->descriptor->thread = thread; g_scheduler.num_threads++; - log(LOG_INFO, " Registered thread %d", thread->id); + log(LOG_INFO, " Registered thread"); - return thread->id; + return thread->descriptor; } -void scheduler_pause_thread(uint32_t id) { - bitmap_set(&g_scheduler.running_threads_bitmap, id, false); - log(LOG_INFO, " Paused thread %d", id); - scheduler_calculate_queue(); +void scheduler_pause_thread(thread_t thread_descriptor) { + scheduler_queue_remove_thread_descriptor((thread_descriptor_T*)thread_descriptor); + log(LOG_INFO, " Paused thread"); } -void scheduler_start_thread(uint32_t id) { - bitmap_set(&g_scheduler.running_threads_bitmap, id, true); - log(LOG_INFO, " Started thread %d", id); - scheduler_calculate_queue(); +void scheduler_start_thread(thread_t thread_descriptor) { + scheduler_queue_add_thread_descriptor((thread_descriptor_T*)thread_descriptor); + log(LOG_INFO, " Started thread"); } -void scheduler_kill_thread(uint32_t id) { - scheduler_pause_thread(id); - bitmap_set(&g_scheduler.threads_bitmap, id, false); +void scheduler_kill_thread(thread_t thread_descriptor) { + scheduler_queue_remove_thread_descriptor((thread_descriptor_T*)thread_descriptor); + memory_free((void*)thread_descriptor); g_scheduler.num_threads--; - log(LOG_INFO, " Killed thread %d", id); + log(LOG_INFO, " Killed thread"); } -thread_T* scheduler_get_thread(uint32_t id) { - if (!bitmap_get(&g_scheduler.threads_bitmap, id)) { - return NULL; - } - - return &g_scheduler.threads[id]; -} - -uint32_t scheduler_get_current_thread() { +thread_t scheduler_get_current_thread() { return g_scheduler.running_thread; } -void scheduler_calculate_queue() { - int index = 0; - - log(LOG_INFO, " Calculating queue:"); - - for (int i = 0; i < g_scheduler.max_threads; i++) { - if (!bitmap_get(&g_scheduler.running_threads_bitmap, i)) { continue; } - - log(LOG_NONE, " > [%d] Added thread %d", index, i); - - g_scheduler.queue[index] = &g_scheduler.threads[i]; - index++; - } - - g_scheduler.queue_length = index; -} - cpu_state_T* scheduler_switch_context(cpu_state_T* state) { if (!g_scheduler.initialized) { - log(LOG_WARNING, "Failed to switch context (scheduler not initialized) INT[0x%xb]", state->interrupt_id); return state; } CORE_HALT_WHILE(g_scheduler.blocked) g_scheduler.blocked = true; - thread_T* old_thread = &g_scheduler.threads[g_scheduler.running_thread]; - thread_T* new_thread = g_scheduler.queue [g_scheduler.queue_index]; - - log(LOG_DEBUG, "Switching Thread OLD[%d] NEW[%d]", old_thread->id, new_thread->id); + thread_T* old_thread = g_scheduler.running_thread->thread; + thread_T* new_thread = g_scheduler.running_thread->next->thread; 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 = new_thread->id; - - g_scheduler.queue_index++; - if (g_scheduler.queue_index == g_scheduler.queue_length) { - g_scheduler.queue_index = 0; - } + g_scheduler.running_thread = g_scheduler.running_thread->next; g_scheduler.blocked = false; return &new_thread->state; diff --git a/kernel/src/proc/thread.c b/kernel/src/proc/thread.c new file mode 100644 index 0000000..3fecdbf --- /dev/null +++ b/kernel/src/proc/thread.c @@ -0,0 +1,81 @@ +/* Copyright (C) Antifallobst + * + * NoxOS is free software: + * you can redistribute it and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * NoxOS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +#include "proc/thread.h" +#include "proc/scheduler.h" +#include "mm/page_map.h" +#include "mm/page_frame.h" +#include "platform/gdt.h" +#include "utils/memory.h" + +thread_t thread_spawn(void* function) { + thread_T* thread = memory_allocate(sizeof(thread_T)); + + thread->stack_size = PFRAME_SIZE * 4; + thread->stack = memory_allocate(thread->stack_size); + thread->cpu_time = 0; + + thread->state = (cpu_state_T){ + .cr3 = (uint64_t)g_kernel_page_map, + .rax = 0, + .rbx = 0, + .rcx = 0, + .rdx = 0, + .rsi = 0, + .rdi = 0, + .rbp = (uint64_t)thread->stack + (thread->stack_size), + .interrupt_id = 0, + .error_code = 0, + .rip = (uint64_t)function, + .cs = GDT_SELECTOR_KERNEL_CODE, + .flags = 1 << CPU_FLAG_INTERRUPT_ENABLE, + .rsp = (uint64_t)thread->stack + (thread->stack_size), + .ss = GDT_SELECTOR_KERNEL_DATA + }; + + return scheduler_register_thread(thread); +} + +thread_t thread_spawn_from_state(cpu_state_T* state) { + thread_T* thread = memory_allocate(sizeof(thread_T)); + + thread->stack_size = 0; + thread->stack = NULL; + thread->cpu_time = 0; + + memory_copy(state, &thread->state, sizeof(cpu_state_T)); + + return scheduler_register_thread(thread); +} + +void thread_start(thread_t thread_descriptor) { + scheduler_start_thread(thread_descriptor); +} + +void thread_pause(thread_t thread_descriptor) { + scheduler_pause_thread(thread_descriptor); +} + +void thread_kill(thread_t thread_descriptor) { + thread_T* thread = thread_descriptor->thread; + + scheduler_kill_thread(thread_descriptor); + + if (thread->stack != NULL) { + memory_free(thread->stack); + } + + memory_free(thread); +} \ No newline at end of file