Compare commits
7 Commits
58eb2d7703
...
293a992133
Author | SHA1 | Date | |
---|---|---|---|
293a992133 | |||
4a7f68b989 | |||
3a1c22bb93 | |||
3ed9e578c7 | |||
96f3d29d37 | |||
42eb0a1d74 | |||
4616997f5b |
@ -26,6 +26,8 @@ extern void pic_eoi(unsigned char irq);
|
||||
extern void pic_eoi(Registers* regs);
|
||||
extern void setup_idt();
|
||||
|
||||
static Thread* g_io_thread;
|
||||
|
||||
void FPData::save()
|
||||
{
|
||||
asm volatile("fxsave (%0)" : : "r"(m_data));
|
||||
@ -126,10 +128,10 @@ void io_thread()
|
||||
while (true)
|
||||
{
|
||||
u8 scancode;
|
||||
while (!scancode_queue.try_pop(scancode)) { kernel_sleep(10); }
|
||||
while (!scancode_queue.try_pop(scancode)) kernel_wait_for_event();
|
||||
|
||||
char key;
|
||||
if (Keyboard::decode_scancode(scancode).try_set_value(key)) { ConsoleDevice::did_press_key(key); }
|
||||
if (Keyboard::decode_scancode(scancode).try_set_value(key)) ConsoleDevice::did_press_key(key);
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,6 +149,7 @@ extern "C" void arch_interrupt_entry(Registers* regs)
|
||||
{
|
||||
u8 scancode = IO::inb(0x60);
|
||||
scancode_queue.try_push(scancode);
|
||||
g_io_thread->wake_up();
|
||||
pic_eoi(regs);
|
||||
}
|
||||
else if (regs->isr == 66) // System call
|
||||
@ -219,8 +222,8 @@ namespace CPU
|
||||
|
||||
void platform_finish_init()
|
||||
{
|
||||
Scheduler::new_kernel_thread(io_thread, "[x86_64-io]")
|
||||
.expect_value("Could not create the IO background thread!");
|
||||
g_io_thread = Scheduler::new_kernel_thread(io_thread, "[x86_64-io]")
|
||||
.expect_value("Could not create the IO background thread!");
|
||||
|
||||
remap_pic();
|
||||
}
|
||||
|
@ -1,10 +1,13 @@
|
||||
#include "fs/devices/ConsoleDevice.h"
|
||||
#include "Log.h"
|
||||
#include "arch/Keyboard.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "thread/Scheduler.h"
|
||||
#include "video/TextConsole.h"
|
||||
#include <bits/termios.h>
|
||||
#include <luna/Buffer.h>
|
||||
#include <luna/CString.h>
|
||||
#include <luna/Units.h>
|
||||
#include <luna/Vector.h>
|
||||
|
||||
static Buffer g_console_input;
|
||||
@ -63,6 +66,27 @@ void ConsoleDevice::did_press_key(char key)
|
||||
return;
|
||||
}
|
||||
|
||||
if (key == 'e' && (Keyboard::modifiers() & (Keyboard::LeftAlt | Keyboard::LeftControl)))
|
||||
{
|
||||
Scheduler::dump_state();
|
||||
return;
|
||||
}
|
||||
|
||||
if (key == 'm' && (Keyboard::modifiers() & (Keyboard::LeftAlt | Keyboard::LeftControl)))
|
||||
{
|
||||
kinfoln("Total memory: %s", to_dynamic_unit(MemoryManager::total()).release_value().chars());
|
||||
kinfoln("Free memory: %s", to_dynamic_unit(MemoryManager::free()).release_value().chars());
|
||||
kinfoln("Used memory: %s", to_dynamic_unit(MemoryManager::used()).release_value().chars());
|
||||
kinfoln("Reserved memory: %s", to_dynamic_unit(MemoryManager::reserved()).release_value().chars());
|
||||
return;
|
||||
}
|
||||
|
||||
if (key == 'h' && (Keyboard::modifiers() & (Keyboard::LeftAlt | Keyboard::LeftControl)))
|
||||
{
|
||||
dump_heap_usage();
|
||||
return;
|
||||
}
|
||||
|
||||
g_temp_input.try_append((u8)key).value();
|
||||
|
||||
if (key == '\n')
|
||||
|
@ -30,7 +30,7 @@ void reap_thread()
|
||||
|
||||
dying_threads.consume([](Thread* thread) { Scheduler::reap_thread(thread); });
|
||||
|
||||
kernel_sleep(250);
|
||||
kernel_wait_for_event();
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,7 +56,8 @@ Result<void> init()
|
||||
auto init = TRY(VFS::resolve_path("/bin/init", Credentials {}));
|
||||
auto init_thread = TRY(Scheduler::new_userspace_thread(init, "/bin/init"));
|
||||
|
||||
Scheduler::new_kernel_thread(reap_thread, "[reap]").release_value();
|
||||
auto reap = Scheduler::new_kernel_thread(reap_thread, "[reap]").release_value();
|
||||
Scheduler::set_reap_thread(reap);
|
||||
|
||||
PCI::scan(
|
||||
[](const PCI::Device& device) {
|
||||
@ -68,7 +69,7 @@ Result<void> init()
|
||||
// Disable console logging before transferring control to userspace.
|
||||
setup_log(log_debug_enabled(), log_serial_enabled(), false);
|
||||
|
||||
init_thread->state = ThreadState::Runnable;
|
||||
init_thread->wake_up();
|
||||
|
||||
kernel_exit();
|
||||
}
|
||||
|
@ -125,12 +125,12 @@ Result<u64> sys_fork(Registers* regs, SyscallArgs)
|
||||
|
||||
thread->state = ThreadState::Runnable;
|
||||
thread->is_kernel = false;
|
||||
thread->parent_id = current->id;
|
||||
thread->fp_data.save();
|
||||
thread->name = current->name;
|
||||
thread->auth = current->auth;
|
||||
thread->current_directory = current->current_directory;
|
||||
thread->current_directory_path = move(current_directory_path);
|
||||
thread->parent = current;
|
||||
|
||||
for (int i = 0; i < FD_MAX; i++) { thread->fd_table[i] = current->fd_table[i]; }
|
||||
|
||||
|
@ -8,10 +8,21 @@ Result<u64> sys_exit(Registers*, SyscallArgs args)
|
||||
Thread* current = Scheduler::current();
|
||||
|
||||
Scheduler::for_each_child(current, [](Thread* child) {
|
||||
child->parent_id = 1;
|
||||
child->parent = Scheduler::init_thread();
|
||||
return true;
|
||||
});
|
||||
|
||||
auto* parent = current->parent;
|
||||
if (parent && parent->state == ThreadState::Waiting)
|
||||
{
|
||||
auto child = *parent->child_being_waited_for;
|
||||
if (child == -1 || child == (pid_t)current->id)
|
||||
{
|
||||
parent->child_being_waited_for = (pid_t)current->id;
|
||||
parent->wake_up();
|
||||
}
|
||||
}
|
||||
|
||||
current->status = status;
|
||||
current->state = ThreadState::Exited;
|
||||
|
||||
|
@ -9,7 +9,9 @@ Result<u64> sys_getpid(Registers*, SyscallArgs)
|
||||
|
||||
Result<u64> sys_getppid(Registers*, SyscallArgs)
|
||||
{
|
||||
return Scheduler::current()->parent_id;
|
||||
auto* parent = Scheduler::current()->parent;
|
||||
|
||||
return parent ? parent->id : 0;
|
||||
}
|
||||
|
||||
Result<u64> sys_getuid(Registers*, SyscallArgs)
|
||||
|
@ -17,35 +17,41 @@ Result<u64> sys_waitpid(Registers*, SyscallArgs args)
|
||||
{
|
||||
thread = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(pid), ESRCH));
|
||||
|
||||
if (thread->parent_id != current->id) return err(ECHILD);
|
||||
if (thread->parent && thread->parent != current) return err(ECHILD);
|
||||
|
||||
while (thread->state != ThreadState::Exited)
|
||||
{
|
||||
if (options & WNOHANG) return err(EAGAIN);
|
||||
kernel_sleep(10);
|
||||
}
|
||||
if (options & WNOHANG) return err(EAGAIN);
|
||||
|
||||
if (thread->state != ThreadState::Exited) kernel_wait(pid);
|
||||
check(thread->state == ThreadState::Exited);
|
||||
}
|
||||
else if (pid == -1)
|
||||
{
|
||||
if (!Scheduler::has_children(current)) return err(ECHILD);
|
||||
|
||||
Option<Thread*> child;
|
||||
|
||||
while (child = Scheduler::find_exited_child(current), !child.has_value())
|
||||
auto child = Scheduler::find_exited_child(current);
|
||||
if (!child.has_value())
|
||||
{
|
||||
if (options & WNOHANG) return err(EAGAIN);
|
||||
kernel_sleep(10);
|
||||
}
|
||||
|
||||
thread = child.value();
|
||||
kernel_wait(pid);
|
||||
check(current->child_being_waited_for.value_or(-1) != -1);
|
||||
|
||||
thread = TRY(Result<Thread*>::from_option(Scheduler::find_by_pid(*current->child_being_waited_for), ESRCH));
|
||||
check(thread->state == ThreadState::Exited);
|
||||
}
|
||||
else
|
||||
thread = child.value();
|
||||
}
|
||||
else
|
||||
return err(ENOTSUP);
|
||||
|
||||
current->child_being_waited_for = {};
|
||||
|
||||
int status = (int)thread->status;
|
||||
u64 id = thread->id;
|
||||
|
||||
thread->state = ThreadState::Dying;
|
||||
Scheduler::signal_reap_thread();
|
||||
|
||||
if (status_ptr)
|
||||
if (!MemoryManager::copy_to_user_typed(status_ptr, &status)) return err(EFAULT);
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
static Thread g_idle;
|
||||
static Thread* g_current = nullptr;
|
||||
static Thread* g_init = nullptr;
|
||||
static Thread* g_reap = nullptr;
|
||||
|
||||
static const usize TICKS_PER_TIMESLICE = 20;
|
||||
|
||||
@ -23,7 +25,7 @@ namespace Scheduler
|
||||
g_idle.set_ip((u64)CPU::idle_loop);
|
||||
g_idle.state = ThreadState::Idle;
|
||||
g_idle.is_kernel = true;
|
||||
g_idle.parent_id = 0;
|
||||
g_idle.parent = nullptr;
|
||||
g_idle.name = "[idle]";
|
||||
|
||||
g_idle.ticks_left = 1;
|
||||
@ -52,7 +54,22 @@ namespace Scheduler
|
||||
return &g_idle;
|
||||
}
|
||||
|
||||
Result<void> new_kernel_thread_impl(Thread* thread, const char* name)
|
||||
Thread* init_thread()
|
||||
{
|
||||
return g_init;
|
||||
}
|
||||
|
||||
void set_reap_thread(Thread* thread)
|
||||
{
|
||||
g_reap = thread;
|
||||
}
|
||||
|
||||
void signal_reap_thread()
|
||||
{
|
||||
if (g_reap) g_reap->wake_up();
|
||||
}
|
||||
|
||||
Result<Thread*> new_kernel_thread_impl(Thread* thread, const char* name)
|
||||
{
|
||||
// If anything fails, make sure to clean up.
|
||||
auto guard = make_scope_guard([&] { delete thread; });
|
||||
@ -66,8 +83,6 @@ namespace Scheduler
|
||||
|
||||
thread->stack = thread_stack;
|
||||
|
||||
thread->parent_id = 0;
|
||||
|
||||
thread->name = name;
|
||||
|
||||
thread->is_kernel = true;
|
||||
@ -80,10 +95,10 @@ namespace Scheduler
|
||||
|
||||
kinfoln("Created kernel thread: id %lu with ip %#lx and sp %#lx", thread->id, thread->ip(), thread->sp());
|
||||
|
||||
return {};
|
||||
return thread;
|
||||
}
|
||||
|
||||
Result<void> new_kernel_thread(u64 address, const char* name)
|
||||
Result<Thread*> new_kernel_thread(u64 address, const char* name)
|
||||
{
|
||||
Thread* const thread = TRY(new_thread());
|
||||
thread->init_regs_kernel();
|
||||
@ -92,7 +107,7 @@ namespace Scheduler
|
||||
return new_kernel_thread_impl(thread, name);
|
||||
}
|
||||
|
||||
Result<void> new_kernel_thread(void (*func)(void), const char* name)
|
||||
Result<Thread*> new_kernel_thread(void (*func)(void), const char* name)
|
||||
{
|
||||
Thread* const thread = TRY(new_thread());
|
||||
thread->init_regs_kernel();
|
||||
@ -101,7 +116,7 @@ namespace Scheduler
|
||||
return new_kernel_thread_impl(thread, name);
|
||||
}
|
||||
|
||||
Result<void> new_kernel_thread(void (*func)(void*), void* arg, const char* name)
|
||||
Result<Thread*> new_kernel_thread(void (*func)(void*), void* arg, const char* name)
|
||||
{
|
||||
Thread* const thread = TRY(new_thread());
|
||||
thread->init_regs_kernel();
|
||||
@ -119,7 +134,6 @@ namespace Scheduler
|
||||
thread->is_kernel = false;
|
||||
thread->id = 1;
|
||||
thread->name = name;
|
||||
thread->parent_id = 0;
|
||||
thread->auth = Credentials { .uid = 0, .euid = 0, .suid = 0, .gid = 0, .egid = 0, .sgid = 0 };
|
||||
|
||||
Vector<String> args;
|
||||
@ -143,6 +157,7 @@ namespace Scheduler
|
||||
thread->sp(), thread->kernel_stack.top());
|
||||
|
||||
g_threads.append(thread);
|
||||
g_init = thread;
|
||||
|
||||
return thread;
|
||||
}
|
||||
@ -253,7 +268,7 @@ namespace Scheduler
|
||||
{
|
||||
if (thread->state == ThreadState::Sleeping)
|
||||
{
|
||||
if (--thread->sleep_ticks_left == 0) thread->state = ThreadState::Runnable;
|
||||
if (--thread->sleep_ticks_left == 0) thread->wake_up();
|
||||
}
|
||||
}
|
||||
|
||||
@ -312,6 +327,28 @@ namespace Scheduler
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void dump_state()
|
||||
{
|
||||
CPU::disable_interrupts();
|
||||
|
||||
kdbgln("--- BEGIN SCHEDULER DUMP ---");
|
||||
kdbgln("current at %p, id = %zu", g_current, g_current->id);
|
||||
|
||||
for (const auto* thread : g_threads)
|
||||
{
|
||||
kdbgln("%p %c [%-20s] %4zu, parent = (%-18p,%zu), state = %d, ticks: (t:%04zu,k:%04zu,u:%04zu), status = "
|
||||
"%d, cwd = %s",
|
||||
thread, thread->is_kernel ? 'k' : 'u', thread->name.chars(), thread->id, thread->parent,
|
||||
thread->parent ? thread->parent->id : 0, (int)thread->state, thread->ticks, thread->ticks_in_kernel,
|
||||
thread->ticks_in_user, thread->status,
|
||||
thread->current_directory_path.is_empty() ? "/" : thread->current_directory_path.chars());
|
||||
}
|
||||
|
||||
kdbgln("--- END SCHEDULER DUMP ---");
|
||||
|
||||
CPU::enable_interrupts();
|
||||
}
|
||||
}
|
||||
|
||||
void kernel_sleep(u64 ms)
|
||||
@ -321,9 +358,23 @@ void kernel_sleep(u64 ms)
|
||||
kernel_yield();
|
||||
}
|
||||
|
||||
void kernel_wait(pid_t pid)
|
||||
{
|
||||
g_current->child_being_waited_for = pid;
|
||||
g_current->state = ThreadState::Waiting;
|
||||
kernel_yield();
|
||||
}
|
||||
|
||||
void kernel_wait_for_event()
|
||||
{
|
||||
g_current->state = ThreadState::Waiting;
|
||||
kernel_yield();
|
||||
}
|
||||
|
||||
[[noreturn]] void kernel_exit()
|
||||
{
|
||||
g_current->state = ThreadState::Dying;
|
||||
Scheduler::signal_reap_thread();
|
||||
kernel_yield();
|
||||
unreachable();
|
||||
}
|
||||
|
@ -8,10 +8,14 @@ namespace Scheduler
|
||||
|
||||
Thread* current();
|
||||
Thread* idle();
|
||||
Thread* init_thread();
|
||||
|
||||
Result<void> new_kernel_thread(u64 address, const char* name);
|
||||
Result<void> new_kernel_thread(void (*func)(void), const char* name);
|
||||
Result<void> new_kernel_thread(void (*func)(void*), void* arg, const char* name);
|
||||
void set_reap_thread(Thread*);
|
||||
void signal_reap_thread();
|
||||
|
||||
Result<Thread*> new_kernel_thread(u64 address, const char* name);
|
||||
Result<Thread*> new_kernel_thread(void (*func)(void), const char* name);
|
||||
Result<Thread*> new_kernel_thread(void (*func)(void*), void* arg, const char* name);
|
||||
|
||||
Result<Thread*> new_userspace_thread(SharedPtr<VFS::Inode> inode, const char* name);
|
||||
|
||||
@ -33,7 +37,7 @@ namespace Scheduler
|
||||
{
|
||||
for (Thread* current = thread; current; current = g_threads.next(current).value_or(nullptr))
|
||||
{
|
||||
if (current->parent_id == thread->id)
|
||||
if (current->parent == thread)
|
||||
{
|
||||
bool should_continue = callback(current);
|
||||
if (!should_continue) return;
|
||||
@ -41,11 +45,17 @@ namespace Scheduler
|
||||
}
|
||||
}
|
||||
|
||||
void dump_state();
|
||||
|
||||
bool has_children(Thread* thread);
|
||||
|
||||
Option<Thread*> find_exited_child(Thread* thread);
|
||||
}
|
||||
|
||||
extern "C" void kernel_yield();
|
||||
void kernel_wait(pid_t pid);
|
||||
void kernel_sleep(u64 ms);
|
||||
[[noreturn]] void kernel_exit();
|
||||
|
||||
// Freezes the current thread until someone else calls wake_up() on this thread.
|
||||
void kernel_wait_for_event();
|
||||
|
@ -22,6 +22,7 @@ enum class ThreadState
|
||||
Idle,
|
||||
Runnable,
|
||||
Sleeping,
|
||||
Waiting,
|
||||
Exited,
|
||||
Dying
|
||||
};
|
||||
@ -55,7 +56,6 @@ struct Thread : public LinkedListNode<Thread>
|
||||
Registers regs;
|
||||
|
||||
u64 id;
|
||||
u64 parent_id;
|
||||
|
||||
Credentials auth;
|
||||
|
||||
@ -90,6 +90,9 @@ struct Thread : public LinkedListNode<Thread>
|
||||
String current_directory_path = {};
|
||||
SharedPtr<VFS::Inode> current_directory = {};
|
||||
|
||||
Thread* parent { nullptr };
|
||||
Option<pid_t> child_being_waited_for = {};
|
||||
|
||||
PageDirectory* directory;
|
||||
|
||||
bool is_idle()
|
||||
@ -97,6 +100,11 @@ struct Thread : public LinkedListNode<Thread>
|
||||
return state == ThreadState::Idle;
|
||||
}
|
||||
|
||||
void wake_up()
|
||||
{
|
||||
state = ThreadState::Runnable;
|
||||
}
|
||||
|
||||
void init_regs_kernel();
|
||||
void init_regs_user();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user