feature (kernel): implemented a basic scheduler

This commit is contained in:
antifallobst 2023-02-18 22:10:27 +01:00
parent 1f342ccea6
commit f6ee1e572f
7 changed files with 304 additions and 6 deletions

View File

@ -0,0 +1,58 @@
/* Copyright (C) Antifallobst <antifallobst@systemausfall.org>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef NOX_SCHEDULER_H
#define NOX_SCHEDULER_H
#include "proc/thread.h"
#include "utils/bitmap.h"
#define SCHEDULER_MAX_PROCESSES 32
#define SCHEDULER_MAX_THREADS_PER_PROCESS 16
typedef struct {
uint32_t max_threads;
uint32_t num_threads;
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;
} 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);
uint32_t scheduler_get_current_thread ();
void scheduler_calculate_queue ();
cpu_state_T* scheduler_switch_context (cpu_state_T* state);
#endif //NOX_SCHEDULER_H

32
kernel/inc/proc/thread.h Normal file
View File

@ -0,0 +1,32 @@
/* Copyright (C) Antifallobst <antifallobst@systemausfall.org>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef NOX_THREAD_H
#define NOX_THREAD_H
#include "utils/stdtypes.h"
#include "platform/cpu.h"
typedef enum {
THREAD_NONE = -1
} thread_standard_E;
typedef struct {
uint32_t id;
cpu_state_T state;
uint64_t cpu_time;
} thread_T;
#endif //NOX_THREAD_H

View File

@ -21,6 +21,10 @@
#include "mm/page_frame.h"
#include "mm/page_map.h"
#include "drivers/time/pit.h"
#include "proc/scheduler.h"
#include "utils/io.h"
#include "platform/gdt.h"
void limine_terminal_print(boot_info_T* boot_info, string_t string) {
boot_info->terminal->write(boot_info->terminal->terminals[0], string, string_length(string));
@ -37,21 +41,54 @@ void kernel_init(boot_info_T* boot_info) {
paging_init();
memory_allocator_init((void*)0x100000000000);
// scheduler_init();
scheduler_init();
}
void test_a() {
while (true) {
io_out_byte(LOG_PORT, 'A');
}
}
void test_b() {
while (true) {
io_out_byte(LOG_PORT, 'B');
}
}
void kmain(boot_info_T boot_info) {
limine_terminal_print(&boot_info, "Booting NoxOS...");
limine_terminal_print(&boot_info, "Booting NoxOS...\n");
log(LOG_DEBUG, "Booting NoxOS");
kernel_init(&boot_info);
limine_terminal_print(&boot_info, "Kernel initialized\n");
log(LOG_INFO, "!=====[ Kernel Initialized ]=====!\n");
asm("int $0x80");
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);
// void* debug = memory_allocate(1312);
test_a();
CORE_HALT_FOREVER
}

View File

@ -21,6 +21,7 @@
#include "mm/page_frame.h"
#include "utils/logger.h"
#include "utils/io.h"
#include "proc/scheduler.h"
idt_register_T g_idt_register;
@ -232,6 +233,7 @@ cpu_state_T* irq_handle(cpu_state_T* state, pic_irq_E irq) {
switch (irq) {
case IRQ_PIT: {
return_state = scheduler_switch_context(state);
// log(LOG_DEBUG, "PIT -> Tick");
break;
}
@ -246,6 +248,7 @@ 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);
}

View File

@ -31,7 +31,7 @@ cpu_state_T* syscall_handle(cpu_state_T* state) {
switch (state->rax) {
case SYSCALL_KERNEL_SCHEDULER_START: {
scheduler_start(state);
return_state = scheduler_start(state);
break;
}

168
kernel/src/proc/scheduler.c Normal file
View File

@ -0,0 +1,168 @@
/* Copyright (C) Antifallobst <antifallobst@systemausfall.org>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "proc/scheduler.h"
#include "utils/memory.h"
#include "utils/logger.h"
#include "utils/core.h"
#include "platform/syscall.h"
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.blocked = false;
syscall_perform(SYSCALL_KERNEL_SCHEDULER_START);
}
cpu_state_T* scheduler_start(cpu_state_T* state) {
thread_T 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);
g_scheduler.initialized = true;
return scheduler_switch_context(state);
}
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;
}
uint32_t scheduler_register_thread(thread_T* thread) {
if (g_scheduler.num_threads >= g_scheduler.max_threads) {
log(LOG_WARNING, "<Scheduler> Failed to register Thread (max thread num reached)");
return THREAD_NONE;
}
thread->id = scheduler_request_thread_id();
if (thread->id == THREAD_NONE) {
log(LOG_WARNING, "<Scheduler> 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));
g_scheduler.num_threads++;
log(LOG_INFO, "<Scheduler> Registered thread %d", thread->id);
return thread->id;
}
void scheduler_pause_thread(uint32_t id) {
bitmap_set(&g_scheduler.running_threads_bitmap, id, false);
log(LOG_INFO, "<Scheduler> Paused thread %d", id);
scheduler_calculate_queue();
}
void scheduler_start_thread(uint32_t id) {
bitmap_set(&g_scheduler.running_threads_bitmap, id, true);
log(LOG_INFO, "<Scheduler> Started thread %d", id);
scheduler_calculate_queue();
}
void scheduler_kill_thread(uint32_t id) {
scheduler_pause_thread(id);
bitmap_set(&g_scheduler.threads_bitmap, id, false);
g_scheduler.num_threads--;
log(LOG_INFO, "<Scheduler> Killed thread %d", id);
}
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() {
return g_scheduler.running_thread;
}
void scheduler_calculate_queue() {
int index = 0;
log(LOG_INFO, "<Scheduler> 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);
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.blocked = false;
return &new_thread->state;
}

View File

@ -36,7 +36,7 @@ void log_send_string_to_port(string_t str) {
}
void log(log_level_E log_level, string_t str, ...) {
CORE_HALT_WHILE(g_logger_blocked)
// CORE_HALT_WHILE(g_logger_blocked)
g_logger_blocked = true;
va_list args;