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/PIT.o \
|
||||
kernel/RTC.o \
|
||||
kernel/Scheduler.o \
|
||||
kernel/Serial.o \
|
||||
kernel/Shell.o \
|
||||
kernel/SpinLock.o \
|
||||
kernel/SSP.o \
|
||||
kernel/Thread.o \
|
||||
kernel/TTY.o \
|
||||
kernel/VesaTerminalDriver.o \
|
||||
icxxabi.o \
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#define PIT_IRQ 0
|
||||
|
||||
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
|
||||
|
||||
#include <kernel/TerminalDriver.h>
|
||||
#include <kernel/SpinLock.h>
|
||||
|
||||
class TTY
|
||||
{
|
||||
|
@ -51,4 +52,5 @@ private:
|
|||
Cell* m_buffer { nullptr };
|
||||
AnsiState m_ansi_state;
|
||||
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/IO.h>
|
||||
#include <kernel/kprint.h>
|
||||
|
||||
#define IRQ_TIMER 0
|
||||
#include <kernel/Scheduler.h>
|
||||
|
||||
#define TIMER0_CTL 0x40
|
||||
#define TIMER1_CTL 0x41
|
||||
|
@ -26,9 +25,10 @@ namespace PIT
|
|||
{
|
||||
static uint64_t s_system_time = 0;
|
||||
|
||||
void clock_handle()
|
||||
void irq_handler()
|
||||
{
|
||||
s_system_time++;
|
||||
Kernel::Scheduler::Get().Switch();
|
||||
}
|
||||
|
||||
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 >> 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/ScopeGuard.h>
|
||||
#include <kernel/Debug.h>
|
||||
#include <kernel/LockGuard.h>
|
||||
#include <kernel/TTY.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)
|
||||
{
|
||||
Kernel::LockGuard guard(m_lock);
|
||||
|
||||
uint16_t cp = handle_unicode(ch);
|
||||
if (cp == 0xFFFF)
|
||||
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/Serial.h>
|
||||
#include <kernel/Shell.h>
|
||||
#include <kernel/Scheduler.h>
|
||||
#include <kernel/TTY.h>
|
||||
#include <kernel/VesaTerminalDriver.h>
|
||||
|
||||
|
@ -55,8 +56,12 @@ ParsedCommandLine ParseCommandLine()
|
|||
return result;
|
||||
}
|
||||
|
||||
static TTY* tty1 = nullptr;
|
||||
|
||||
extern "C" void kernel_main()
|
||||
{
|
||||
using namespace Kernel;
|
||||
|
||||
DISABLE_INTERRUPTS();
|
||||
|
||||
auto cmdline = ParseCommandLine();
|
||||
|
@ -82,7 +87,7 @@ extern "C" void kernel_main()
|
|||
TerminalDriver* terminal_driver = VesaTerminalDriver::Create();
|
||||
ASSERT(terminal_driver);
|
||||
dprintln("VESA initialized");
|
||||
TTY* tty1 = new TTY(terminal_driver);
|
||||
tty1 = new TTY(terminal_driver);
|
||||
|
||||
InterruptController::Initialize(cmdline.force_pic);
|
||||
dprintln("Interrupt controller initialized");
|
||||
|
@ -93,15 +98,18 @@ extern "C" void kernel_main()
|
|||
return;
|
||||
dprintln("8042 initialized");
|
||||
|
||||
ENABLE_INTERRUPTS();
|
||||
|
||||
kprintln("Hello from the kernel!");
|
||||
|
||||
Kernel::Shell shell(tty1);
|
||||
shell.Run();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
asm("hlt");
|
||||
}
|
||||
Scheduler::Initialize();
|
||||
Scheduler& scheduler = Scheduler::Get();
|
||||
scheduler.AddThread([](){ Shell(tty1).Run(); });
|
||||
scheduler.AddThread(
|
||||
[]()
|
||||
{
|
||||
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