Kernel: Implement somewhat functioning multithread support
This still uses only a single cpu, but we can now have 'parallelization' This seems to work fine in qemu and bochs, but my own computer did not like this when I last tried. I have absolutely no idea how multithreading should actually be implmemented and I just thought and implemented the most simple one I could think of. This might not be in any way correct :D
This commit is contained in:
parent
7d8aafa0b5
commit
6a9d60a8fb
|
@ -43,10 +43,12 @@ kernel/kmalloc.o \
|
||||||
kernel/PIC.o \
|
kernel/PIC.o \
|
||||||
kernel/PIT.o \
|
kernel/PIT.o \
|
||||||
kernel/RTC.o \
|
kernel/RTC.o \
|
||||||
|
kernel/Scheduler.o \
|
||||||
kernel/Serial.o \
|
kernel/Serial.o \
|
||||||
kernel/Shell.o \
|
kernel/Shell.o \
|
||||||
kernel/SpinLock.o \
|
kernel/SpinLock.o \
|
||||||
kernel/SSP.o \
|
kernel/SSP.o \
|
||||||
|
kernel/Thread.o \
|
||||||
kernel/TTY.o \
|
kernel/TTY.o \
|
||||||
kernel/VesaTerminalDriver.o \
|
kernel/VesaTerminalDriver.o \
|
||||||
icxxabi.o \
|
icxxabi.o \
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define PIT_IRQ 0
|
||||||
|
|
||||||
namespace PIT
|
namespace PIT
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/LinkedList.h>
|
||||||
|
#include <kernel/Thread.h>
|
||||||
|
|
||||||
|
namespace Kernel
|
||||||
|
{
|
||||||
|
|
||||||
|
class Scheduler
|
||||||
|
{
|
||||||
|
BAN_NON_COPYABLE(Scheduler);
|
||||||
|
BAN_NON_MOVABLE(Scheduler);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void Initialize();
|
||||||
|
static Scheduler& Get();
|
||||||
|
|
||||||
|
const Thread& CurrentThread() const;
|
||||||
|
|
||||||
|
void AddThread(void(*)());
|
||||||
|
void Switch();
|
||||||
|
void Start();
|
||||||
|
|
||||||
|
static constexpr size_t ms_between_switch = 4;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Scheduler() {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
BAN::LinkedList<Thread> m_threads;
|
||||||
|
BAN::LinkedList<Thread>::iterator m_current_iterator;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <kernel/TerminalDriver.h>
|
#include <kernel/TerminalDriver.h>
|
||||||
|
#include <kernel/SpinLock.h>
|
||||||
|
|
||||||
class TTY
|
class TTY
|
||||||
{
|
{
|
||||||
|
@ -51,4 +52,5 @@ private:
|
||||||
Cell* m_buffer { nullptr };
|
Cell* m_buffer { nullptr };
|
||||||
AnsiState m_ansi_state;
|
AnsiState m_ansi_state;
|
||||||
TerminalDriver* m_terminal_driver { nullptr };
|
TerminalDriver* m_terminal_driver { nullptr };
|
||||||
|
Kernel::SpinLock m_lock;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Memory.h>
|
||||||
|
|
||||||
|
namespace Kernel
|
||||||
|
{
|
||||||
|
|
||||||
|
class Thread
|
||||||
|
{
|
||||||
|
BAN_NON_COPYABLE(Thread);
|
||||||
|
BAN_NON_MOVABLE(Thread);
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class State
|
||||||
|
{
|
||||||
|
NotStarted,
|
||||||
|
Running,
|
||||||
|
Paused,
|
||||||
|
Done,
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
Thread(void(*)());
|
||||||
|
~Thread();
|
||||||
|
|
||||||
|
uint32_t id() const { return m_id; }
|
||||||
|
|
||||||
|
void set_rip(uintptr_t rip) { m_rip = rip; }
|
||||||
|
void set_rsp(uintptr_t rsp) { m_rsp = rsp; }
|
||||||
|
void set_state(State state) { m_state = state; }
|
||||||
|
uintptr_t rip() const { return m_rip; }
|
||||||
|
uintptr_t rsp() const { return m_rsp; }
|
||||||
|
State state() const { return m_state; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void on_exit();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void* m_stack_base = nullptr;
|
||||||
|
State m_state = State::NotStarted;
|
||||||
|
uintptr_t m_rip = 0;
|
||||||
|
uintptr_t m_rsp = 0;
|
||||||
|
const uint32_t m_id = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -2,8 +2,7 @@
|
||||||
#include <kernel/InterruptController.h>
|
#include <kernel/InterruptController.h>
|
||||||
#include <kernel/IO.h>
|
#include <kernel/IO.h>
|
||||||
#include <kernel/kprint.h>
|
#include <kernel/kprint.h>
|
||||||
|
#include <kernel/Scheduler.h>
|
||||||
#define IRQ_TIMER 0
|
|
||||||
|
|
||||||
#define TIMER0_CTL 0x40
|
#define TIMER0_CTL 0x40
|
||||||
#define TIMER1_CTL 0x41
|
#define TIMER1_CTL 0x41
|
||||||
|
@ -26,9 +25,10 @@ namespace PIT
|
||||||
{
|
{
|
||||||
static uint64_t s_system_time = 0;
|
static uint64_t s_system_time = 0;
|
||||||
|
|
||||||
void clock_handle()
|
void irq_handler()
|
||||||
{
|
{
|
||||||
s_system_time++;
|
s_system_time++;
|
||||||
|
Kernel::Scheduler::Get().Switch();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t ms_since_boot()
|
uint64_t ms_since_boot()
|
||||||
|
@ -45,9 +45,9 @@ namespace PIT
|
||||||
IO::outb(TIMER0_CTL, (timer_reload >> 0) & 0xff);
|
IO::outb(TIMER0_CTL, (timer_reload >> 0) & 0xff);
|
||||||
IO::outb(TIMER0_CTL, (timer_reload >> 8) & 0xff);
|
IO::outb(TIMER0_CTL, (timer_reload >> 8) & 0xff);
|
||||||
|
|
||||||
IDT::register_irq_handler(IRQ_TIMER, clock_handle);
|
IDT::register_irq_handler(PIT_IRQ, irq_handler);
|
||||||
|
|
||||||
InterruptController::Get().EnableIrq(IRQ_TIMER);
|
InterruptController::Get().EnableIrq(PIT_IRQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
#include <kernel/Arch.h>
|
||||||
|
#include <kernel/InterruptController.h>
|
||||||
|
#include <kernel/Scheduler.h>
|
||||||
|
|
||||||
|
namespace Kernel
|
||||||
|
{
|
||||||
|
|
||||||
|
static Scheduler* s_instance = nullptr;
|
||||||
|
|
||||||
|
extern "C" uintptr_t read_rip();
|
||||||
|
asm(
|
||||||
|
".global read_rip;"
|
||||||
|
"read_rip:"
|
||||||
|
#if ARCH(x86_64)
|
||||||
|
"popq %rax;"
|
||||||
|
"jmp *%rax"
|
||||||
|
#else
|
||||||
|
"popl %eax;"
|
||||||
|
"jmp *%eax"
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
|
||||||
|
void Scheduler::Initialize()
|
||||||
|
{
|
||||||
|
ASSERT(!s_instance);
|
||||||
|
s_instance = new Scheduler();
|
||||||
|
}
|
||||||
|
|
||||||
|
Scheduler& Scheduler::Get()
|
||||||
|
{
|
||||||
|
ASSERT(s_instance);
|
||||||
|
return *s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Thread& Scheduler::CurrentThread() const
|
||||||
|
{
|
||||||
|
return *m_current_iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scheduler::AddThread(void(*thread)())
|
||||||
|
{
|
||||||
|
MUST(m_threads.EmplaceBack(thread));
|
||||||
|
if (!m_current_iterator)
|
||||||
|
m_current_iterator = m_threads.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scheduler::Switch()
|
||||||
|
{
|
||||||
|
static uint8_t cnt = 0;
|
||||||
|
if (cnt++ % ms_between_switch)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ASSERT(InterruptController::Get().IsInService(PIT_IRQ));
|
||||||
|
|
||||||
|
ASSERT(m_threads.Size() > 0);
|
||||||
|
if (m_threads.Size() == 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ASSERT(m_current_iterator);
|
||||||
|
|
||||||
|
auto next_iterator = m_current_iterator;
|
||||||
|
if (++next_iterator == m_threads.end())
|
||||||
|
next_iterator = m_threads.begin();
|
||||||
|
|
||||||
|
Thread& current = *m_current_iterator;
|
||||||
|
Thread& next = *next_iterator;
|
||||||
|
|
||||||
|
if (current.state() == Thread::State::Done)
|
||||||
|
{
|
||||||
|
// NOTE: this does not invalidate the next/next_iterator
|
||||||
|
// since we are working with linked list
|
||||||
|
m_threads.Remove(m_current_iterator);
|
||||||
|
m_current_iterator = decltype(m_threads)::iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t rip = read_rip();
|
||||||
|
if (rip == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uintptr_t rsp;
|
||||||
|
#if ARCH(x86_64)
|
||||||
|
asm volatile("movq %%rsp, %0" : "=r"(rsp));
|
||||||
|
#else
|
||||||
|
asm volatile("movl %%esp, %0" : "=r"(rsp));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (m_current_iterator)
|
||||||
|
{
|
||||||
|
current.set_rip(rip);
|
||||||
|
current.set_rsp(rsp);
|
||||||
|
current.set_state(Thread::State::Paused);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_current_iterator = next_iterator;
|
||||||
|
|
||||||
|
if (next.state() == Thread::State::NotStarted)
|
||||||
|
{
|
||||||
|
InterruptController::Get().EOI(PIT_IRQ);
|
||||||
|
next.set_state(Thread::State::Running);
|
||||||
|
asm volatile(
|
||||||
|
#if ARCH(x86_64)
|
||||||
|
"movq %0, %%rsp;"
|
||||||
|
#else
|
||||||
|
"movl %0, %%esp;"
|
||||||
|
#endif
|
||||||
|
"sti;"
|
||||||
|
"jmp *%1;"
|
||||||
|
:: "r"(next.rsp()), "r"(next.rip())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (next.state() == Thread::State::Paused)
|
||||||
|
{
|
||||||
|
next.set_state(Thread::State::Running);
|
||||||
|
asm volatile(
|
||||||
|
#if ARCH(x86_64)
|
||||||
|
"movq %0, %%rsp;"
|
||||||
|
"movq $0, %%rax;"
|
||||||
|
#else
|
||||||
|
"movl %0, %%esp;"
|
||||||
|
"movl $0, %%eax;"
|
||||||
|
#endif
|
||||||
|
"jmp *%1;"
|
||||||
|
:: "r"(next.rsp()), "r"(next.rip())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scheduler::Start()
|
||||||
|
{
|
||||||
|
ASSERT(m_threads.Size() > 0);
|
||||||
|
ASSERT(m_current_iterator);
|
||||||
|
|
||||||
|
Thread& current = *m_current_iterator;
|
||||||
|
ASSERT(current.state() == Thread::State::NotStarted);
|
||||||
|
current.set_state(Thread::State::Running);
|
||||||
|
asm volatile(
|
||||||
|
#if ARCH(x86_64)
|
||||||
|
"movq %0, %%rsp;"
|
||||||
|
#else
|
||||||
|
"movl %0, %%esp;"
|
||||||
|
#endif
|
||||||
|
"sti;"
|
||||||
|
"jmp *%1;"
|
||||||
|
:: "r"(current.rsp()), "r"(current.rip())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
|
#include <BAN/ScopeGuard.h>
|
||||||
#include <kernel/Debug.h>
|
#include <kernel/Debug.h>
|
||||||
|
#include <kernel/LockGuard.h>
|
||||||
#include <kernel/TTY.h>
|
#include <kernel/TTY.h>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -261,6 +263,8 @@ void TTY::PutCharAt(uint16_t ch, uint32_t x, uint32_t y)
|
||||||
|
|
||||||
void TTY::PutChar(char ch)
|
void TTY::PutChar(char ch)
|
||||||
{
|
{
|
||||||
|
Kernel::LockGuard guard(m_lock);
|
||||||
|
|
||||||
uint16_t cp = handle_unicode(ch);
|
uint16_t cp = handle_unicode(ch);
|
||||||
if (cp == 0xFFFF)
|
if (cp == 0xFFFF)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include <BAN/Errors.h>
|
||||||
|
#include <kernel/Arch.h>
|
||||||
|
#include <kernel/InterruptController.h>
|
||||||
|
#include <kernel/kmalloc.h>
|
||||||
|
#include <kernel/Scheduler.h>
|
||||||
|
#include <kernel/Thread.h>
|
||||||
|
|
||||||
|
#define PAGE_SIZE 4096
|
||||||
|
|
||||||
|
namespace Kernel
|
||||||
|
{
|
||||||
|
|
||||||
|
static uint32_t s_next_id = 1;
|
||||||
|
|
||||||
|
static constexpr size_t thread_stack_size = PAGE_SIZE;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static void write_to_stack(uintptr_t& rsp, const T& value)
|
||||||
|
{
|
||||||
|
rsp -= sizeof(T);
|
||||||
|
*(T*)rsp = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread::Thread(void(*function)())
|
||||||
|
: m_id(s_next_id++)
|
||||||
|
{
|
||||||
|
m_stack_base = kmalloc(thread_stack_size, PAGE_SIZE);
|
||||||
|
ASSERT(m_stack_base);
|
||||||
|
|
||||||
|
m_rip = (uintptr_t)function;
|
||||||
|
m_rsp = (uintptr_t)m_stack_base + thread_stack_size;
|
||||||
|
write_to_stack(m_rsp, this);
|
||||||
|
write_to_stack(m_rsp, &Thread::on_exit);
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread::~Thread()
|
||||||
|
{
|
||||||
|
kfree(m_stack_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::on_exit()
|
||||||
|
{
|
||||||
|
Thread* thread = nullptr;
|
||||||
|
#if ARCH(x86_64)
|
||||||
|
asm volatile("movq (%%rsp), %0" : "=r"(thread));
|
||||||
|
#else
|
||||||
|
asm volatile("movl (%%esp), %0" : "=r"(thread));
|
||||||
|
#endif
|
||||||
|
thread->m_state = State::Done;
|
||||||
|
for (;;) asm volatile("hlt");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -10,6 +10,7 @@
|
||||||
#include <kernel/PIT.h>
|
#include <kernel/PIT.h>
|
||||||
#include <kernel/Serial.h>
|
#include <kernel/Serial.h>
|
||||||
#include <kernel/Shell.h>
|
#include <kernel/Shell.h>
|
||||||
|
#include <kernel/Scheduler.h>
|
||||||
#include <kernel/TTY.h>
|
#include <kernel/TTY.h>
|
||||||
#include <kernel/VesaTerminalDriver.h>
|
#include <kernel/VesaTerminalDriver.h>
|
||||||
|
|
||||||
|
@ -55,8 +56,12 @@ ParsedCommandLine ParseCommandLine()
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static TTY* tty1 = nullptr;
|
||||||
|
|
||||||
extern "C" void kernel_main()
|
extern "C" void kernel_main()
|
||||||
{
|
{
|
||||||
|
using namespace Kernel;
|
||||||
|
|
||||||
DISABLE_INTERRUPTS();
|
DISABLE_INTERRUPTS();
|
||||||
|
|
||||||
auto cmdline = ParseCommandLine();
|
auto cmdline = ParseCommandLine();
|
||||||
|
@ -82,7 +87,7 @@ extern "C" void kernel_main()
|
||||||
TerminalDriver* terminal_driver = VesaTerminalDriver::Create();
|
TerminalDriver* terminal_driver = VesaTerminalDriver::Create();
|
||||||
ASSERT(terminal_driver);
|
ASSERT(terminal_driver);
|
||||||
dprintln("VESA initialized");
|
dprintln("VESA initialized");
|
||||||
TTY* tty1 = new TTY(terminal_driver);
|
tty1 = new TTY(terminal_driver);
|
||||||
|
|
||||||
InterruptController::Initialize(cmdline.force_pic);
|
InterruptController::Initialize(cmdline.force_pic);
|
||||||
dprintln("Interrupt controller initialized");
|
dprintln("Interrupt controller initialized");
|
||||||
|
@ -93,15 +98,18 @@ extern "C" void kernel_main()
|
||||||
return;
|
return;
|
||||||
dprintln("8042 initialized");
|
dprintln("8042 initialized");
|
||||||
|
|
||||||
ENABLE_INTERRUPTS();
|
Scheduler::Initialize();
|
||||||
|
Scheduler& scheduler = Scheduler::Get();
|
||||||
kprintln("Hello from the kernel!");
|
scheduler.AddThread([](){ Shell(tty1).Run(); });
|
||||||
|
scheduler.AddThread(
|
||||||
Kernel::Shell shell(tty1);
|
[]()
|
||||||
shell.Run();
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
{
|
||||||
asm("hlt");
|
uint64_t start = PIT::ms_since_boot();
|
||||||
|
while (PIT::ms_since_boot() < start + 3000)
|
||||||
|
continue;
|
||||||
|
kprintln("\nHello!");
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
scheduler.Start();
|
||||||
|
ASSERT(false);
|
||||||
}
|
}
|
Loading…
Reference in New Issue