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,26 +87,29 @@ 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"); | ||||
| 	 | ||||
| 
 | ||||
| 	PIT::initialize(); | ||||
| 	dprintln("PIT initialized"); | ||||
| 	if (!Input::initialize()) | ||||
| 		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