diff options
author | Andreas Kling <awesomekling@gmail.com> | 2019-03-23 22:03:17 +0100 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-03-23 22:03:17 +0100 |
commit | 60d25f0f4a7bfeab18366e66b9ef0b648b5c8ace (patch) | |
tree | c0c815ffe60934791175b3087550d0a5f6add93f | |
parent | b0de6aa8d82f117da3d5617a2df6d52742f6809c (diff) | |
download | serenity-60d25f0f4a7bfeab18366e66b9ef0b648b5c8ace.zip |
Kernel: Introduce threads, and refactor everything in support of it.
The scheduler now operates on threads, rather than on processes.
Each process has a main thread, and can have any number of additional
threads. The process exits when the main thread exits.
This patch doesn't actually spawn any additional threads, it merely
does all the plumbing needed to make it possible. :^)
32 files changed, 1355 insertions, 1097 deletions
diff --git a/AK/DoublyLinkedList.h b/AK/DoublyLinkedList.h index 3bd8899a3b..14661acf0e 100644 --- a/AK/DoublyLinkedList.h +++ b/AK/DoublyLinkedList.h @@ -1,6 +1,7 @@ #pragma once #include "StdLibExtras.h" +#include <AK/Assertions.h> namespace AK { diff --git a/Kernel/DiskBackedFileSystem.cpp b/Kernel/DiskBackedFileSystem.cpp index 5b79955230..f6214bb8b3 100644 --- a/Kernel/DiskBackedFileSystem.cpp +++ b/Kernel/DiskBackedFileSystem.cpp @@ -1,6 +1,7 @@ #include "DiskBackedFileSystem.h" #include "i386.h" #include <AK/InlineLRUCache.h> +#include <Kernel/Process.h> //#define DBFS_DEBUG diff --git a/Kernel/DiskDevice.cpp b/Kernel/DiskDevice.cpp index a0c655e6d9..d7157485ad 100644 --- a/Kernel/DiskDevice.cpp +++ b/Kernel/DiskDevice.cpp @@ -10,7 +10,6 @@ DiskDevice::~DiskDevice() bool DiskDevice::read(DiskOffset offset, unsigned length, byte* out) const { - //kprintf("DD::read %u x%u\n", offset, length); ASSERT((offset % block_size()) == 0); ASSERT((length % block_size()) == 0); dword first_block = offset / block_size(); diff --git a/Kernel/Ext2FileSystem.cpp b/Kernel/Ext2FileSystem.cpp index a5cfcd2123..7ae1bb65a6 100644 --- a/Kernel/Ext2FileSystem.cpp +++ b/Kernel/Ext2FileSystem.cpp @@ -1182,8 +1182,8 @@ RetainPtr<Inode> Ext2FS::create_inode(InodeIdentifier parent_id, const String& n ext2_inode e2inode; memset(&e2inode, 0, sizeof(ext2_inode)); e2inode.i_mode = mode; - e2inode.i_uid = current->euid(); - e2inode.i_gid = current->egid(); + e2inode.i_uid = current->process().euid(); + e2inode.i_gid = current->process().egid(); e2inode.i_size = size; e2inode.i_atime = timestamp; e2inode.i_ctime = timestamp; diff --git a/Kernel/IDEDiskDevice.cpp b/Kernel/IDEDiskDevice.cpp index ba70225cd7..1b0eb27061 100644 --- a/Kernel/IDEDiskDevice.cpp +++ b/Kernel/IDEDiskDevice.cpp @@ -178,8 +178,8 @@ bool IDEDiskDevice::read_sectors(dword start_sector, word count, byte* outbuf) { LOCKER(m_lock); #ifdef DISK_DEBUG - kprintf("%s: Disk::read_sectors request (%u sector(s) @ %u)\n", - current->name().characters(), + dbgprintf("%s: Disk::read_sectors request (%u sector(s) @ %u)\n", + current->process().name().characters(), count, start_sector); #endif @@ -231,7 +231,7 @@ bool IDEDiskDevice::write_sectors(dword start_sector, word count, const byte* da LOCKER(m_lock); #ifdef DISK_DEBUG dbgprintf("%s(%u): IDEDiskDevice::write_sectors request (%u sector(s) @ %u)\n", - current->name().characters(), + current->process().name().characters(), current->pid(), count, start_sector); diff --git a/Kernel/IPv4Socket.cpp b/Kernel/IPv4Socket.cpp index 63ad0ae80a..3d3ea4664b 100644 --- a/Kernel/IPv4Socket.cpp +++ b/Kernel/IPv4Socket.cpp @@ -33,7 +33,7 @@ Retained<IPv4Socket> IPv4Socket::create(int type, int protocol) IPv4Socket::IPv4Socket(int type, int protocol) : Socket(AF_INET, type, protocol) { - kprintf("%s(%u) IPv4Socket{%p} created with type=%u, protocol=%d\n", current->name().characters(), current->pid(), this, type, protocol); + kprintf("%s(%u) IPv4Socket{%p} created with type=%u, protocol=%d\n", current->process().name().characters(), current->pid(), this, type, protocol); LOCKER(all_sockets().lock()); all_sockets().resource().set(this); } @@ -189,7 +189,7 @@ ssize_t IPv4Socket::recvfrom(void* buffer, size_t buffer_length, int flags, sock current->set_blocked_socket(this); load_receive_deadline(); - block(Process::BlockedReceive); + block(Thread::BlockedReceive); Scheduler::yield(); LOCKER(lock()); diff --git a/Kernel/KSyms.cpp b/Kernel/KSyms.cpp index a9adf83a0b..875a6991ed 100644 --- a/Kernel/KSyms.cpp +++ b/Kernel/KSyms.cpp @@ -74,7 +74,7 @@ static void load_ksyms_from_data(const ByteBuffer& buffer) [[gnu::noinline]] void dump_backtrace_impl(dword ebp, bool use_ksyms) { if (!current) { - hang(); + //hang(); return; } if (use_ksyms && !ksyms_ready) { @@ -87,13 +87,13 @@ static void load_ksyms_from_data(const ByteBuffer& buffer) }; Vector<RecognizedSymbol> recognized_symbols; if (use_ksyms) { - for (dword* stack_ptr = (dword*)ebp; current->validate_read_from_kernel(LinearAddress((dword)stack_ptr)); stack_ptr = (dword*)*stack_ptr) { + for (dword* stack_ptr = (dword*)ebp; current->process().validate_read_from_kernel(LinearAddress((dword)stack_ptr)); stack_ptr = (dword*)*stack_ptr) { dword retaddr = stack_ptr[1]; if (auto* ksym = ksymbolicate(retaddr)) recognized_symbols.append({ retaddr, ksym }); } } else{ - for (dword* stack_ptr = (dword*)ebp; current->validate_read_from_kernel(LinearAddress((dword)stack_ptr)); stack_ptr = (dword*)*stack_ptr) { + for (dword* stack_ptr = (dword*)ebp; current->process().validate_read_from_kernel(LinearAddress((dword)stack_ptr)); stack_ptr = (dword*)*stack_ptr) { dword retaddr = stack_ptr[1]; kprintf("%x (next: %x)\n", retaddr, stack_ptr ? (dword*)*stack_ptr : 0); } @@ -129,7 +129,7 @@ void load_ksyms() auto result = VFS::the().open("/kernel.map", 0, 0, *VFS::the().root_inode()); ASSERT(!result.is_error()); auto descriptor = result.value(); - auto buffer = descriptor->read_entire_file(*current); + auto buffer = descriptor->read_entire_file(current->process()); ASSERT(buffer); load_ksyms_from_data(buffer); } diff --git a/Kernel/LocalSocket.cpp b/Kernel/LocalSocket.cpp index 1f7209bdd5..d14cf3ca13 100644 --- a/Kernel/LocalSocket.cpp +++ b/Kernel/LocalSocket.cpp @@ -15,7 +15,7 @@ LocalSocket::LocalSocket(int type) : Socket(AF_LOCAL, type, 0) { #ifdef DEBUG_LOCAL_SOCKET - kprintf("%s(%u) LocalSocket{%p} created with type=%u\n", current->name().characters(), current->pid(), this, type); + kprintf("%s(%u) LocalSocket{%p} created with type=%u\n", current->process().name().characters(), current->pid(), this, type); #endif } @@ -46,10 +46,10 @@ KResult LocalSocket::bind(const sockaddr* address, socklen_t address_size) memcpy(safe_address, local_address.sun_path, sizeof(local_address.sun_path)); #ifdef DEBUG_LOCAL_SOCKET - kprintf("%s(%u) LocalSocket{%p} bind(%s)\n", current->name().characters(), current->pid(), this, safe_address); + kprintf("%s(%u) LocalSocket{%p} bind(%s)\n", current->process().name().characters(), current->pid(), this, safe_address); #endif - auto result = VFS::the().open(safe_address, O_CREAT | O_EXCL, S_IFSOCK | 0666, current->cwd_inode()); + auto result = VFS::the().open(safe_address, O_CREAT | O_EXCL, S_IFSOCK | 0666, current->process().cwd_inode()); if (result.is_error()) { if (result.error() == -EEXIST) return KResult(-EADDRINUSE); @@ -78,10 +78,10 @@ KResult LocalSocket::connect(const sockaddr* address, socklen_t address_size) memcpy(safe_address, local_address.sun_path, sizeof(local_address.sun_path)); #ifdef DEBUG_LOCAL_SOCKET - kprintf("%s(%u) LocalSocket{%p} connect(%s)\n", current->name().characters(), current->pid(), this, safe_address); + kprintf("%s(%u) LocalSocket{%p} connect(%s)\n", current->process().name().characters(), current->pid(), this, safe_address); #endif - auto descriptor_or_error = VFS::the().open(safe_address, 0, 0, current->cwd_inode()); + auto descriptor_or_error = VFS::the().open(safe_address, 0, 0, current->process().cwd_inode()); if (descriptor_or_error.is_error()) return KResult(-ECONNREFUSED); m_file = move(descriptor_or_error.value()); diff --git a/Kernel/Lock.h b/Kernel/Lock.h index 4854ce7ccb..c91c13faeb 100644 --- a/Kernel/Lock.h +++ b/Kernel/Lock.h @@ -5,8 +5,8 @@ #include <Kernel/i386.h> #include <Kernel/Scheduler.h> -class Process; -extern Process* current; +class Thread; +extern Thread* current; static inline dword CAS(volatile dword* mem, dword newval, dword oldval) { @@ -32,7 +32,7 @@ public: private: volatile dword m_lock { 0 }; dword m_level { 0 }; - Process* m_holder { nullptr }; + Thread* m_holder { nullptr }; const char* m_name { nullptr }; }; diff --git a/Kernel/Makefile b/Kernel/Makefile index 5d42acab6d..c57ac37d9c 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -5,6 +5,7 @@ KERNEL_OBJS = \ StdLib.o \ i386.o \ Process.o \ + Thread.o \ i8253.o \ KeyboardDevice.o \ CMOS.o \ diff --git a/Kernel/MasterPTY.cpp b/Kernel/MasterPTY.cpp index 819277edd6..b727830612 100644 --- a/Kernel/MasterPTY.cpp +++ b/Kernel/MasterPTY.cpp @@ -11,8 +11,8 @@ MasterPTY::MasterPTY(unsigned index) , m_slave(adopt(*new SlavePTY(*this, index))) , m_index(index) { - set_uid(current->uid()); - set_gid(current->gid()); + set_uid(current->process().uid()); + set_gid(current->process().gid()); } MasterPTY::~MasterPTY() diff --git a/Kernel/MemoryManager.cpp b/Kernel/MemoryManager.cpp index 1ce601bcc8..e5720473e7 100644 --- a/Kernel/MemoryManager.cpp +++ b/Kernel/MemoryManager.cpp @@ -36,6 +36,8 @@ MemoryManager::MemoryManager() m_page_table_zero = (dword*)0x6000; initialize_paging(); + + kprintf("MM initialized.\n"); } MemoryManager::~MemoryManager() @@ -109,6 +111,10 @@ void MemoryManager::initialize_paging() "orl $0x80000001, %%eax\n" "movl %%eax, %%cr0\n" :::"%eax", "memory"); + +#ifdef MM_DEBUG + dbgprintf("MM: Paging initialized.\n"); +#endif } RetainPtr<PhysicalPage> MemoryManager::allocate_page_table(PageDirectory& page_directory, unsigned index) @@ -362,11 +368,12 @@ bool MemoryManager::page_in_from_inode(Region& region, unsigned page_index_in_re PageFaultResponse MemoryManager::handle_page_fault(const PageFault& fault) { ASSERT_INTERRUPTS_DISABLED(); + ASSERT(current); #ifdef PAGE_FAULT_DEBUG dbgprintf("MM: handle_page_fault(%w) at L%x\n", fault.code(), fault.laddr().get()); #endif ASSERT(fault.laddr() != m_quickmap_addr); - auto* region = region_from_laddr(*current, fault.laddr()); + auto* region = region_from_laddr(current->process(), fault.laddr()); if (!region) { kprintf("NP(error) fault at invalid address L%x\n", fault.laddr().get()); return PageFaultResponse::ShouldCrash; @@ -441,8 +448,9 @@ RetainPtr<PhysicalPage> MemoryManager::allocate_supervisor_physical_page() void MemoryManager::enter_process_paging_scope(Process& process) { + ASSERT(current); InterruptDisabler disabler; - current->m_tss.cr3 = process.page_directory().cr3(); + current->tss().cr3 = process.page_directory().cr3(); asm volatile("movl %%eax, %%cr3"::"a"(process.page_directory().cr3()):"memory"); } @@ -620,9 +628,10 @@ bool MemoryManager::validate_user_write(const Process& process, LinearAddress la Retained<Region> Region::clone() { + ASSERT(current); if (m_shared || (m_readable && !m_writable)) { dbgprintf("%s<%u> Region::clone(): sharing %s (L%x)\n", - current->name().characters(), + current->process().name().characters(), current->pid(), m_name.characters(), laddr().get()); @@ -631,14 +640,14 @@ Retained<Region> Region::clone() } dbgprintf("%s<%u> Region::clone(): cowing %s (L%x)\n", - current->name().characters(), + current->process().name().characters(), current->pid(), m_name.characters(), laddr().get()); // Set up a COW region. The parent (this) region becomes COW as well! for (size_t i = 0; i < page_count(); ++i) m_cow_map.set(i, true); - MM.remap_region(current->page_directory(), *this); + MM.remap_region(current->process().page_directory(), *this); return adopt(*new Region(laddr(), size(), m_vmo->clone(), m_offset_in_vmo, String(m_name), m_readable, m_writable, true)); } @@ -966,6 +975,33 @@ PageDirectory::~PageDirectory() void PageDirectory::flush(LinearAddress laddr) { - if (¤t->page_directory() == this) +#ifdef MM_DEBUG + dbgprintf("MM: Flush page L%x\n", laddr.get()); +#endif + if (!current) + return; + if (¤t->process().page_directory() == this) MM.flush_tlb(laddr); } + +ProcessPagingScope::ProcessPagingScope(Process& process) +{ + ASSERT(current); + MM.enter_process_paging_scope(process); +} + +ProcessPagingScope::~ProcessPagingScope() +{ + MM.enter_process_paging_scope(current->process()); +} + +KernelPagingScope::KernelPagingScope() +{ + ASSERT(current); + MM.enter_kernel_paging_scope(); +} + +KernelPagingScope::~KernelPagingScope() +{ + MM.enter_process_paging_scope(current->process()); +} diff --git a/Kernel/MemoryManager.h b/Kernel/MemoryManager.h index a045ff1270..60e7fc8c9b 100644 --- a/Kernel/MemoryManager.h +++ b/Kernel/MemoryManager.h @@ -15,8 +15,6 @@ #define PAGE_ROUND_UP(x) ((((dword)(x)) + PAGE_SIZE-1) & (~(PAGE_SIZE-1))) -class Process; -extern Process* current; class SynthFSInode; enum class PageFaultResponse { @@ -411,11 +409,11 @@ private: }; struct ProcessPagingScope { - ProcessPagingScope(Process& process) { MM.enter_process_paging_scope(process); } - ~ProcessPagingScope() { MM.enter_process_paging_scope(*current); } + ProcessPagingScope(Process&); + ~ProcessPagingScope(); }; struct KernelPagingScope { - KernelPagingScope() { MM.enter_kernel_paging_scope(); } - ~KernelPagingScope() { MM.enter_process_paging_scope(*current); } + KernelPagingScope(); + ~KernelPagingScope(); }; diff --git a/Kernel/ProcFS.cpp b/Kernel/ProcFS.cpp index e59929ef3d..bfa183a49f 100644 --- a/Kernel/ProcFS.cpp +++ b/Kernel/ProcFS.cpp @@ -292,19 +292,24 @@ ByteBuffer procfs$pid_stack(InodeIdentifier identifier) dword address; const KSym* ksym; }; - Vector<RecognizedSymbol> recognized_symbols; - if (auto* eip_ksym = ksymbolicate(process.tss().eip)) - recognized_symbols.append({ process.tss().eip, eip_ksym }); - for (dword* stack_ptr = (dword*)process.frame_ptr(); process.validate_read_from_kernel(LinearAddress((dword)stack_ptr)); stack_ptr = (dword*)*stack_ptr) { - dword retaddr = stack_ptr[1]; - if (auto* ksym = ksymbolicate(retaddr)) - recognized_symbols.append({ retaddr, ksym }); - } StringBuilder builder; - for (auto& symbol : recognized_symbols) { - unsigned offset = symbol.address - symbol.ksym->address; - builder.appendf("%p %s +%u\n", symbol.address, symbol.ksym->name, offset); - } + process.for_each_thread([&] (Thread& thread) { + builder.appendf("Thread %d:\n", thread.tid()); + Vector<RecognizedSymbol> recognized_symbols; + if (auto* eip_ksym = ksymbolicate(thread.tss().eip)) + recognized_symbols.append({ thread.tss().eip, eip_ksym }); + for (dword* stack_ptr = (dword*)thread.frame_ptr(); process.validate_read_from_kernel(LinearAddress((dword)stack_ptr)); stack_ptr = (dword*)*stack_ptr) { + dword retaddr = stack_ptr[1]; + if (auto* ksym = ksymbolicate(retaddr)) + recognized_symbols.append({ retaddr, ksym }); + } + + for (auto& symbol : recognized_symbols) { + unsigned offset = symbol.address - symbol.ksym->address; + builder.appendf("%p %s +%u\n", symbol.address, symbol.ksym->name, offset); + } + return IterationDecision::Continue; + }); return builder.to_byte_buffer(); } @@ -314,19 +319,23 @@ ByteBuffer procfs$pid_regs(InodeIdentifier identifier) if (!handle) return { }; auto& process = handle->process(); - auto& tss = process.tss(); StringBuilder builder; - builder.appendf("eax: %x\n", tss.eax); - builder.appendf("ebx: %x\n", tss.ebx); - builder.appendf("ecx: %x\n", tss.ecx); - builder.appendf("edx: %x\n", tss.edx); - builder.appendf("esi: %x\n", tss.esi); - builder.appendf("edi: %x\n", tss.edi); - builder.appendf("ebp: %x\n", tss.ebp); - builder.appendf("cr3: %x\n", tss.cr3); - builder.appendf("flg: %x\n", tss.eflags); - builder.appendf("sp: %w:%x\n", tss.ss, tss.esp); - builder.appendf("pc: %w:%x\n", tss.cs, tss.eip); + process.for_each_thread([&] (Thread& thread) { + builder.appendf("Thread %d:\n", thread.tid()); + auto& tss = thread.tss(); + builder.appendf("eax: %x\n", tss.eax); + builder.appendf("ebx: %x\n", tss.ebx); + builder.appendf("ecx: %x\n", tss.ecx); + builder.appendf("edx: %x\n", tss.edx); + builder.appendf("esi: %x\n", tss.esi); + builder.appendf("edi: %x\n", tss.edi); + builder.appendf("ebp: %x\n", tss.ebp); + builder.appendf("cr3: %x\n", tss.cr3); + builder.appendf("flg: %x\n", tss.eflags); + builder.appendf("sp: %w:%x\n", tss.ss, tss.esp); + builder.appendf("pc: %w:%x\n", tss.cs, tss.eip); + return IterationDecision::Continue; + }); return builder.to_byte_buffer(); } @@ -530,7 +539,7 @@ ByteBuffer procfs$summary(InodeIdentifier) process->uid(), to_string(process->state()), process->ppid(), - process->times_scheduled(), + process->main_thread().times_scheduled(), // FIXME(Thread): Bill all scheds to the process process->number_of_open_file_descriptors(), process->tty() ? strrchr(process->tty()->tty_name().characters(), '/') + 1 : "n/a", process->name().characters()); @@ -562,7 +571,7 @@ ByteBuffer procfs$all(InodeIdentifier) auto build_process_line = [&builder] (Process* process) { builder.appendf("%u,%u,%u,%u,%u,%u,%u,%s,%u,%u,%s,%s,%u,%u,%u,%u,%s\n", process->pid(), - process->times_scheduled(), + process->main_thread().times_scheduled(), // FIXME(Thread): Bill all scheds to the process process->tty() ? process->tty()->pgid() : 0, process->pgid(), process->sid(), @@ -576,7 +585,7 @@ ByteBuffer procfs$all(InodeIdentifier) process->amount_virtual(), process->amount_resident(), process->amount_shared(), - process->ticks(), + process->main_thread().ticks(), // FIXME(Thread): Bill all ticks to the process to_string(process->priority()) ); }; diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 60f67afba2..4ac23417ad 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -27,15 +27,12 @@ #include <Kernel/ARP.h> //#define DEBUG_IO -//#define TASK_DEBUG -//#define FORK_DEBUG -//#define SIGNAL_DEBUG +#define TASK_DEBUG +#define FORK_DEBUG +#define SIGNAL_DEBUG #define MAX_PROCESS_GIDS 32 //#define SHARED_BUFFER_DEBUG -static const dword default_kernel_stack_size = 16384; -static const dword default_userspace_stack_size = 65536; - static pid_t next_pid; InlineLinkedList<Process>* g_processes; static String* s_hostname; @@ -52,7 +49,6 @@ void Process::initialize() g_processes = new InlineLinkedList<Process>; s_hostname = new String("courage"); s_hostname_lock = new Lock; - Scheduler::initialize(); } Vector<pid_t> Process::all_pids() @@ -226,8 +222,6 @@ Process* Process::fork(RegisterDump& regs) if (!child) return nullptr; - memcpy(child->m_signal_action_data, m_signal_action_data, sizeof(m_signal_action_data)); - child->m_signal_mask = m_signal_mask; #ifdef FORK_DEBUG dbgprintf("fork: child=%p\n", child); #endif @@ -239,35 +233,31 @@ Process* Process::fork(RegisterDump& regs) auto cloned_region = region->clone(); child->m_regions.append(move(cloned_region)); MM.map_region(*child, *child->m_regions.last()); - if (region.ptr() == m_display_framebuffer_region.ptr()) - child->m_display_framebuffer_region = child->m_regions.last().copy_ref(); } for (auto gid : m_gids) child->m_gids.set(gid); - child->m_tss.eax = 0; // fork() returns 0 in the child :^) - child->m_tss.ebx = regs.ebx; - child->m_tss.ecx = regs.ecx; - child->m_tss.edx = regs.edx; - child->m_tss.ebp = regs.ebp; - child->m_tss.esp = regs.esp_if_crossRing; - child->m_tss.esi = regs.esi; - child->m_tss.edi = regs.edi; - child->m_tss.eflags = regs.eflags; - child->m_tss.eip = regs.eip; - child->m_tss.cs = regs.cs; - child->m_tss.ds = regs.ds; - child->m_tss.es = regs.es; - child->m_tss.fs = regs.fs; - child->m_tss.gs = regs.gs; - child->m_tss.ss = regs.ss_if_crossRing; - - child->m_fpu_state = m_fpu_state; - child->m_has_used_fpu = m_has_used_fpu; + auto& child_tss = child->main_thread().m_tss; + child_tss.eax = 0; // fork() returns 0 in the child :^) + child_tss.ebx = regs.ebx; + child_tss.ecx = regs.ecx; + child_tss.edx = regs.edx; + child_tss.ebp = regs.ebp; + child_tss.esp = regs.esp_if_crossRing; + child_tss.esi = regs.esi; + child_tss.edi = regs.edi; + child_tss.eflags = regs.eflags; + child_tss.eip = regs.eip; + child_tss.cs = regs.cs; + child_tss.ds = regs.ds; + child_tss.es = regs.es; + child_tss.fs = regs.fs; + child_tss.gs = regs.gs; + child_tss.ss = regs.ss_if_crossRing; #ifdef FORK_DEBUG - dbgprintf("fork: child will begin executing at %w:%x with stack %w:%x\n", child->m_tss.cs, child->m_tss.eip, child->m_tss.ss, child->m_tss.esp); + dbgprintf("fork: child will begin executing at %w:%x with stack %w:%x, kstack %w:%x\n", child_tss.cs, child_tss.eip, child_tss.ss, child_tss.esp, child_tss.ss0, child_tss.esp0); #endif { @@ -276,8 +266,11 @@ Process* Process::fork(RegisterDump& regs) system.nprocess++; } #ifdef TASK_DEBUG - kprintf("Process %u (%s) forked from %u @ %p\n", child->pid(), child->name().characters(), m_pid, child->m_tss.eip); + kprintf("Process %u (%s) forked from %u @ %p\n", child->pid(), child->name().characters(), m_pid, child_tss.eip); #endif + + child->main_thread().set_state(Thread::State::Skip1SchedulerPass); + return child; } @@ -292,6 +285,9 @@ int Process::do_exec(String path, Vector<String> arguments, Vector<String> envir { ASSERT(is_ring3()); + // FIXME(Thread): Kill any threads the moment we commit to the exec(). + ASSERT(thread_count() == 1); + auto parts = path.split('/'); if (parts.is_empty()) return -ENOENT; @@ -305,7 +301,6 @@ int Process::do_exec(String path, Vector<String> arguments, Vector<String> envir return -EACCES; if (!descriptor->metadata().size) { - kprintf("exec() of 0-length binaries not supported\n"); return -ENOTIMPL; } @@ -355,7 +350,7 @@ int Process::do_exec(String path, Vector<String> arguments, Vector<String> envir if (!success) { m_page_directory = move(old_page_directory); // FIXME: RAII this somehow instead. - ASSERT(current == this); + ASSERT(¤t->process() == this); MM.enter_process_paging_scope(*this); m_regions = move(old_regions); kprintf("sys$execve: Failure loading %s\n", path.characters()); @@ -366,20 +361,19 @@ int Process::do_exec(String path, Vector<String> arguments, Vector<String> envir if (!entry_eip) { m_page_directory = move(old_page_directory); // FIXME: RAII this somehow instead. - ASSERT(current == this); + ASSERT(¤t->process() == this); MM.enter_process_paging_scope(*this); m_regions = move(old_regions); return -ENOEXEC; } } - kfree(m_kernel_stack_for_signal_handler); - m_kernel_stack_for_signal_handler = nullptr; - m_signal_stack_user_region = nullptr; - m_display_framebuffer_region = nullptr; - set_default_signal_dispositions(); - m_signal_mask = 0; - m_pending_signals = 0; + kfree(current->m_kernel_stack_for_signal_handler); + current->m_kernel_stack_for_signal_handler = nullptr; + current->m_signal_stack_user_region = nullptr; + current->set_default_signal_dispositions(); + current->m_signal_mask = 0; + current->m_pending_signals = 0; for (int i = 0; i < m_fds.size(); ++i) { auto& daf = m_fds[i]; @@ -392,29 +386,30 @@ int Process::do_exec(String path, Vector<String> arguments, Vector<String> envir // We cli() manually here because we don't want to get interrupted between do_exec() and Schedule::yield(). // The reason is that the task redirection we've set up above will be clobbered by the timer IRQ. // If we used an InterruptDisabler that sti()'d on exit, we might timer tick'd too soon in exec(). - if (current == this) + if (¤t->process() == this) cli(); - Scheduler::prepare_to_modify_tss(*this); + Scheduler::prepare_to_modify_tss(main_thread()); m_name = parts.take_last(); - dword old_esp0 = m_tss.esp0; - - memset(&m_tss, 0, sizeof(m_tss)); - m_tss.eflags = 0x0202; - m_tss.eip = entry_eip; - m_tss.cs = 0x1b; - m_tss.ds = 0x23; - m_tss.es = 0x23; - m_tss.fs = 0x23; - m_tss.gs = 0x23; - m_tss.ss = 0x23; - m_tss.cr3 = page_directory().cr3(); - make_userspace_stack(move(arguments), move(environment)); - m_tss.ss0 = 0x10; - m_tss.esp0 = old_esp0; - m_tss.ss2 = m_pid; + // ss0 sp!!!!!!!!! + dword old_esp0 = main_thread().m_tss.esp0; + + memset(&main_thread().m_tss, 0, sizeof(main_thread().m_tss)); + main_thread().m_tss.eflags = 0x0202; + main_thread().m_tss.eip = entry_eip; + main_thread().m_tss.cs = 0x1b; + main_thread().m_tss.ds = 0x23; + main_thread().m_tss.es = 0x23; + main_thread().m_tss.fs = 0x23; + main_thread().m_tss.gs = 0x23; + main_thread().m_tss.ss = 0x23; + main_thread().m_tss.cr3 = page_directory().cr3(); + main_thread().make_userspace_stack(move(arguments), move(environment)); + main_thread().m_tss.ss0 = 0x10; + main_thread().m_tss.esp0 = old_esp0; + main_thread().m_tss.ss2 = m_pid; m_executable = descriptor->inode(); @@ -424,60 +419,13 @@ int Process::do_exec(String path, Vector<String> arguments, Vector<String> envir m_egid = descriptor->metadata().gid; #ifdef TASK_DEBUG - kprintf("Process %u (%s) exec'd %s @ %p\n", pid(), name().characters(), path.characters(), m_tss.eip); + kprintf("Process %u (%s) exec'd %s @ %p\n", pid(), name().characters(), path.characters(), main_thread().tss().eip); #endif - set_state(Skip1SchedulerPass); + main_thread().set_state(Thread::State::Skip1SchedulerPass); return 0; } -void Process::make_userspace_stack(Vector<String> arguments, Vector<String> environment) -{ - auto* region = allocate_region(LinearAddress(), default_userspace_stack_size, "stack"); - ASSERT(region); - m_stack_top3 = region->laddr().offset(default_userspace_stack_size).get(); - m_tss.esp = m_stack_top3; - - char* stack_base = (char*)region->laddr().get(); - int argc = arguments.size(); - char** argv = (char**)stack_base; - char** env = argv + arguments.size() + 1; - char* bufptr = stack_base + (sizeof(char*) * (arguments.size() + 1)) + (sizeof(char*) * (environment.size() + 1)); - - size_t total_blob_size = 0; - for (auto& a : arguments) - total_blob_size += a.length() + 1; - for (auto& e : environment) - total_blob_size += e.length() + 1; - - size_t total_meta_size = sizeof(char*) * (arguments.size() + 1) + sizeof(char*) * (environment.size() + 1); - - // FIXME: It would be better if this didn't make us panic. - ASSERT((total_blob_size + total_meta_size) < default_userspace_stack_size); - - for (int i = 0; i < arguments.size(); ++i) { - argv[i] = bufptr; - memcpy(bufptr, arguments[i].characters(), arguments[i].length()); - bufptr += arguments[i].length(); - *(bufptr++) = '\0'; - } - argv[arguments.size()] = nullptr; - - for (int i = 0; i < environment.size(); ++i) { - env[i] = bufptr; - memcpy(bufptr, environment[i].characters(), environment[i].length()); - bufptr += environment[i].length(); - *(bufptr++) = '\0'; - } - env[environment.size()] = nullptr; - - // NOTE: The stack needs to be 16-byte aligned. - push_value_on_stack((dword)env); - push_value_on_stack((dword)argv); - push_value_on_stack((dword)argc); - push_value_on_stack(0); -} - int Process::exec(String path, Vector<String> arguments, Vector<String> environment) { // The bulk of exec() is done by do_exec(), which ensures that all locals @@ -486,7 +434,7 @@ int Process::exec(String path, Vector<String> arguments, Vector<String> environm if (rc < 0) return rc; - if (current == this) { + if (¤t->process() == this) { Scheduler::yield(); ASSERT_NOT_REACHED(); } @@ -571,7 +519,7 @@ Process* Process::create_user_process(const String& path, uid_t uid, gid_t gid, system.nprocess++; } #ifdef TASK_DEBUG - kprintf("Process %u (%s) spawned @ %p\n", process->pid(), process->name().characters(), process->m_tss.eip); + kprintf("Process %u (%s) spawned @ %p\n", process->pid(), process->name().characters(), process->main_thread().tss().eip); #endif error = 0; return process; @@ -580,19 +528,18 @@ Process* Process::create_user_process(const String& path, uid_t uid, gid_t gid, Process* Process::create_kernel_process(String&& name, void (*e)()) { auto* process = new Process(move(name), (uid_t)0, (gid_t)0, (pid_t)0, Ring0); - process->m_tss.eip = (dword)e; + process->main_thread().tss().eip = (dword)e; if (process->pid() != 0) { - { - InterruptDisabler disabler; - g_processes->prepend(process); - system.nprocess++; - } + InterruptDisabler disabler; + g_processes->prepend(process); + system.nprocess++; #ifdef TASK_DEBUG - kprintf("Kernel process %u (%s) spawned @ %p\n", process->pid(), process->name().characters(), process->m_tss.eip); + kprintf("Kernel process %u (%s) spawned @ %p\n", process->pid(), process->name().characters(), process->main_thread().tss().eip); #endif } + process->main_thread().set_state(Thread::State::Runnable); return process; } @@ -603,16 +550,24 @@ Process::Process(String&& name, uid_t uid, gid_t gid, pid_t ppid, RingLevel ring , m_gid(gid) , m_euid(uid) , m_egid(gid) - , m_state(Runnable) , m_ring(ring) , m_cwd(move(cwd)) , m_executable(move(executable)) , m_tty(tty) , m_ppid(ppid) { - set_default_signal_dispositions(); + dbgprintf("Process: New process PID=%u with name=%s\n", m_pid, m_name.characters()); + + m_page_directory = PageDirectory::create(); +#ifdef MM_DEBUG + dbgprintf("Process %u ctor: PD=%x created\n", pid(), m_page_directory.ptr()); +#endif - memset(&m_fpu_state, 0, sizeof(FPUState)); + // NOTE: fork() doesn't clone all threads; the thread that called fork() becomes the main thread in the new process. + if (fork_parent) + m_main_thread = current->clone(*this); + else + m_main_thread = new Thread(*this); m_gids.set(m_gid); @@ -628,11 +583,6 @@ Process::Process(String&& name, uid_t uid, gid_t gid, pid_t ppid, RingLevel ring } } - m_page_directory = PageDirectory::create(); -#ifdef MM_DEBUG - dbgprintf("Process %u ctor: PD=%x created\n", pid(), m_page_directory.ptr()); -#endif - if (fork_parent) { m_fds.resize(fork_parent->m_fds.size()); for (int i = 0; i < fork_parent->m_fds.size(); ++i) { @@ -657,82 +607,22 @@ Process::Process(String&& name, uid_t uid, gid_t gid, pid_t ppid, RingLevel ring else m_next_region = LinearAddress(0x10000000); - if (fork_parent) { - memcpy(&m_tss, &fork_parent->m_tss, sizeof(m_tss)); - } else { - memset(&m_tss, 0, sizeof(m_tss)); - - // Only IF is set when a process boots. - m_tss.eflags = 0x0202; - word cs, ds, ss; - - if (is_ring0()) { - cs = 0x08; - ds = 0x10; - ss = 0x10; - } else { - cs = 0x1b; - ds = 0x23; - ss = 0x23; - } - - m_tss.ds = ds; - m_tss.es = ds; - m_tss.fs = ds; - m_tss.gs = ds; - m_tss.ss = ss; - m_tss.cs = cs; - } - - m_tss.cr3 = page_directory().cr3(); - - if (is_ring0()) { - // FIXME: This memory is leaked. - // But uh, there's also no kernel process termination, so I guess it's not technically leaked... - dword stack_bottom = (dword)kmalloc_eternal(default_kernel_stack_size); - m_stack_top0 = (stack_bottom + default_kernel_stack_size) & 0xffffff8; - m_tss.esp = m_stack_top0; - } else { - // Ring3 processes need a separate stack for Ring0. - m_kernel_stack = kmalloc(default_kernel_stack_size); - m_stack_top0 = ((dword)m_kernel_stack + default_kernel_stack_size) & 0xffffff8; - m_tss.ss0 = 0x10; - m_tss.esp0 = m_stack_top0; - } if (fork_parent) { m_sid = fork_parent->m_sid; m_pgid = fork_parent->m_pgid; m_umask = fork_parent->m_umask; } - - // HACK: Ring2 SS in the TSS is the current PID. - m_tss.ss2 = m_pid; - m_far_ptr.offset = 0x98765432; } Process::~Process() { - { - InterruptDisabler disabler; - system.nprocess--; - } - - if (g_last_fpu_process == this) - g_last_fpu_process = nullptr; - - if (selector()) - gdt_free_entry(selector()); - - if (m_kernel_stack) { - kfree(m_kernel_stack); - m_kernel_stack = nullptr; - } + dbgprintf("~Process{%p} name=%s pid=%d\n", this, m_name.characters(), pid()); + InterruptDisabler disabler; + system.nprocess--; - if (m_kernel_stack_for_signal_handler) { - kfree(m_kernel_stack_for_signal_handler); - m_kernel_stack_for_signal_handler = nullptr; - } + delete m_main_thread; + m_main_thread = nullptr; } void Process::dump_regions() @@ -761,242 +651,6 @@ void Process::sys$exit(int status) ASSERT_NOT_REACHED(); } -void Process::terminate_due_to_signal(byte signal) -{ - ASSERT_INTERRUPTS_DISABLED(); - ASSERT(signal < 32); - dbgprintf("terminate_due_to_signal %s(%u) <- %u\n", name().characters(), pid(), signal); - m_termination_status = 0; - m_termination_signal = signal; - die(); -} - -void Process::send_signal(byte signal, Process* sender) -{ - ASSERT(signal < 32); - - if (sender) - dbgprintf("signal: %s(%u) sent %d to %s(%u)\n", sender->name().characters(), sender->pid(), signal, name().characters(), pid()); - else - dbgprintf("signal: kernel sent %d to %s(%u)\n", signal, name().characters(), pid()); - - InterruptDisabler disabler; - m_pending_signals |= 1 << signal; -} - -bool Process::has_unmasked_pending_signals() const -{ - return m_pending_signals & ~m_signal_mask; -} - -ShouldUnblockProcess Process::dispatch_one_pending_signal() -{ - ASSERT_INTERRUPTS_DISABLED(); - dword signal_candidates = m_pending_signals & ~m_signal_mask; - ASSERT(signal_candidates); - - byte signal = 0; - for (; signal < 32; ++signal) { - if (signal_candidates & (1 << signal)) { - break; - } - } - return dispatch_signal(signal); -} - -enum class DefaultSignalAction { - Terminate, - Ignore, - DumpCore, - Stop, - Continue, -}; - -DefaultSignalAction default_signal_action(byte signal) -{ - ASSERT(signal && signal < NSIG); - - switch (signal) { - case SIGHUP: - case SIGINT: - case SIGKILL: - case SIGPIPE: - case SIGALRM: - case SIGUSR1: - case SIGUSR2: - case SIGVTALRM: - case SIGSTKFLT: - case SIGIO: - case SIGPROF: - case SIGTERM: - case SIGPWR: - return DefaultSignalAction::Terminate; - case SIGCHLD: - case SIGURG: - case SIGWINCH: - return DefaultSignalAction::Ignore; - case SIGQUIT: - case SIGILL: - case SIGTRAP: - case SIGABRT: - case SIGBUS: - case SIGFPE: - case SIGSEGV: - case SIGXCPU: - case SIGXFSZ: - case SIGSYS: - return DefaultSignalAction::DumpCore; - case SIGCONT: - return DefaultSignalAction::Continue; - case SIGSTOP: - case SIGTSTP: - case SIGTTIN: - case SIGTTOU: - return DefaultSignalAction::Stop; - } - ASSERT_NOT_REACHED(); -} - -ShouldUnblockProcess Process::dispatch_signal(byte signal) -{ - ASSERT_INTERRUPTS_DISABLED(); - ASSERT(signal < 32); - -#ifdef SIGNAL_DEBUG - kprintf("dispatch_signal %s(%u) <- %u\n", name().characters(), pid(), signal); -#endif - - auto& action = m_signal_action_data[signal]; - // FIXME: Implement SA_SIGINFO signal handlers. - ASSERT(!(action.flags & SA_SIGINFO)); - - // Mark this signal as handled. - m_pending_signals &= ~(1 << signal); - - if (signal == SIGSTOP) { - set_state(Stopped); - return ShouldUnblockProcess::No; - } - - if (signal == SIGCONT && state() == Stopped) - set_state(Runnable); - - auto handler_laddr = action.handler_or_sigaction; - if (handler_laddr.is_null()) { - switch (default_signal_action(signal)) { - case DefaultSignalAction::Stop: - set_state(Stopped); - return ShouldUnblockProcess::No; - case DefaultSignalAction::DumpCore: - case DefaultSignalAction::Terminate: - terminate_due_to_signal(signal); - return ShouldUnblockProcess::No; - case DefaultSignalAction::Ignore: - return ShouldUnblockProcess::No; - case DefaultSignalAction::Continue: - return ShouldUnblockProcess::Yes; - } - ASSERT_NOT_REACHED(); - } - - if (handler_laddr.as_ptr() == SIG_IGN) { -#ifdef SIGNAL_DEBUG - kprintf("%s(%u) ignored signal %u\n", name().characters(), pid(), signal); -#endif - return ShouldUnblockProcess::Yes; - } - - dword old_signal_mask = m_signal_mask; - dword new_signal_mask = action.mask; - if (action.flags & SA_NODEFER) - new_signal_mask &= ~(1 << signal); - else - new_signal_mask |= 1 << signal; - - m_signal_mask |= new_signal_mask; - - Scheduler::prepare_to_modify_tss(*this); - - word ret_cs = m_tss.cs; - dword ret_eip = m_tss.eip; - dword ret_eflags = m_tss.eflags; - bool interrupting_in_kernel = (ret_cs & 3) == 0; - - ProcessPagingScope paging_scope(*this); - create_signal_trampolines_if_needed(); - - if (interrupting_in_kernel) { -#ifdef SIGNAL_DEBUG - kprintf("dispatch_signal to %s(%u) in state=%s with return to %w:%x\n", name().characters(), pid(), to_string(state()), ret_cs, ret_eip); -#endif - ASSERT(is_blocked()); - m_tss_to_resume_kernel = m_tss; -#ifdef SIGNAL_DEBUG - kprintf("resume tss pc: %w:%x stack: %w:%x flags: %x cr3: %x\n", m_tss_to_resume_kernel.cs, m_tss_to_resume_kernel.eip, m_tss_to_resume_kernel.ss, m_tss_to_resume_kernel.esp, m_tss_to_resume_kernel.eflags, m_tss_to_resume_kernel.cr3); -#endif - - if (!m_signal_stack_user_region) { - m_signal_stack_user_region = allocate_region(LinearAddress(), default_userspace_stack_size, "Signal stack (user)"); - ASSERT(m_signal_stack_user_region); - } - if (!m_kernel_stack_for_signal_handler) { - m_kernel_stack_for_signal_handler = kmalloc(default_kernel_stack_size); - ASSERT(m_kernel_stack_for_signal_handler); - } - m_tss.ss = 0x23; - m_tss.esp = m_signal_stack_user_region->laddr().offset(default_userspace_stack_size).get(); - m_tss.ss0 = 0x10; - m_tss.esp0 = (dword)m_kernel_stack_for_signal_handler + default_kernel_stack_size; - - push_value_on_stack(0); - } else { - push_value_on_stack(ret_eip); - push_value_on_stack(ret_eflags); - - // PUSHA - dword old_esp = m_tss.esp; - push_value_on_stack(m_tss.eax); - push_value_on_stack(m_tss.ecx); - push_value_on_stack(m_tss.edx); - push_value_on_stack(m_tss.ebx); - push_value_on_stack(old_esp); - push_value_on_stack(m_tss.ebp); - push_value_on_stack(m_tss.esi); - push_value_on_stack(m_tss.edi); - - // Align the stack. - m_tss.esp -= 12; - } - - // PUSH old_signal_mask - push_value_on_stack(old_signal_mask); - - m_tss.cs = 0x1b; - m_tss.ds = 0x23; - m_tss.es = 0x23; - m_tss.fs = 0x23; - m_tss.gs = 0x23; - m_tss.eip = handler_laddr.get(); - - // FIXME: Should we worry about the stack being 16 byte aligned when entering a signal handler? - push_value_on_stack(signal); - - if (interrupting_in_kernel) - push_value_on_stack(m_return_to_ring0_from_signal_trampoline.get()); - else - push_value_on_stack(m_return_to_ring3_from_signal_trampoline.get()); - - ASSERT((m_tss.esp % 16) == 0); - - // FIXME: This state is such a hack. It avoids trouble if 'current' is the process receiving a signal. - set_state(Skip1SchedulerPass); - -#ifdef SIGNAL_DEBUG - kprintf("signal: Okay, %s(%u) {%s} has been primed with signal handler %w:%x\n", name().characters(), pid(), to_string(state()), m_tss.cs, m_tss.eip); -#endif - return ShouldUnblockProcess::Yes; -} - void Process::create_signal_trampolines_if_needed() { if (!m_return_to_ring3_from_signal_trampoline.is_null()) @@ -1045,36 +699,30 @@ void Process::create_signal_trampolines_if_needed() int Process::sys$restore_signal_mask(dword mask) { - m_signal_mask = mask; + current->m_signal_mask = mask; return 0; } void Process::sys$sigreturn() { InterruptDisabler disabler; - Scheduler::prepare_to_modify_tss(*this); - m_tss = m_tss_to_resume_kernel; + Scheduler::prepare_to_modify_tss(*current); + current->m_tss = current->m_tss_to_resume_kernel; #ifdef SIGNAL_DEBUG kprintf("sys$sigreturn in %s(%u)\n", name().characters(), pid()); - kprintf(" -> resuming execution at %w:%x stack %w:%x flags %x cr3 %x\n", m_tss.cs, m_tss.eip, m_tss.ss, m_tss.esp, m_tss.eflags, m_tss.cr3); + auto& tss = current->tss(); + kprintf(" -> resuming execution at %w:%x stack %w:%x flags %x cr3 %x\n", tss.cs, tss.eip, tss.ss, tss.esp, tss.eflags, tss.cr3); #endif - set_state(Skip1SchedulerPass); + current->set_state(Thread::State::Skip1SchedulerPass); Scheduler::yield(); kprintf("sys$sigreturn failed in %s(%u)\n", name().characters(), pid()); ASSERT_NOT_REACHED(); } -void Process::push_value_on_stack(dword value) -{ - m_tss.esp -= 4; - dword* stack_ptr = (dword*)m_tss.esp; - *stack_ptr = value; -} - void Process::crash() { ASSERT_INTERRUPTS_DISABLED(); - ASSERT(state() != Dead); + ASSERT(!is_dead()); m_termination_signal = SIGSEGV; dump_regions(); @@ -1190,8 +838,8 @@ ssize_t Process::sys$write(int fd, const byte* data, ssize_t size) #ifdef IO_DEBUG dbgprintf("block write on %d\n", fd); #endif - m_blocked_fd = fd; - block(BlockedWrite); + current->m_blocked_fd = fd; + current->block(Thread::State::BlockedWrite); Scheduler::yield(); } ssize_t rc = descriptor->write(*this, (const byte*)data + nwritten, size - nwritten); @@ -1205,8 +853,8 @@ ssize_t Process::sys$write(int fd, const byte* data, ssize_t size) } if (rc == 0) break; - if (has_unmasked_pending_signals()) { - block(BlockedSignal); + if (current->has_unmasked_pending_signals()) { + current->block(Thread::State::BlockedSignal); Scheduler::yield(); if (nwritten == 0) return -EINTR; @@ -1216,8 +864,8 @@ ssize_t Process::sys$write(int fd, const byte* data, ssize_t size) } else { nwritten = descriptor->write(*this, (const byte*)data, size); } - if (has_unmasked_pending_signals()) { - block(BlockedSignal); + if (current->has_unmasked_pending_signals()) { + current->block(Thread::State::BlockedSignal); Scheduler::yield(); if (nwritten == 0) return -EINTR; @@ -1239,10 +887,10 @@ ssize_t Process::sys$read(int fd, byte* buffer, ssize_t size) return -EBADF; if (descriptor->is_blocking()) { if (!descriptor->can_read(*this)) { - m_blocked_fd = fd; - block(BlockedRead); + current->m_blocked_fd = fd; + current->block(Thread::State::BlockedRead); Scheduler::yield(); - if (m_was_interrupted_while_blocked) + if (current->m_was_interrupted_while_blocked) return -EINTR; } } @@ -1545,7 +1193,7 @@ int Process::sys$kill(pid_t pid, int signal) ASSERT(pid != -1); } if (pid == m_pid) { - send_signal(signal, this); + current->send_signal(signal, this); Scheduler::yield(); return 0; } @@ -1571,9 +1219,9 @@ int Process::sys$usleep(useconds_t usec) return 0; sleep(usec / 1000); - if (m_wakeup_time > system.uptime) { - ASSERT(m_was_interrupted_while_blocked); - dword ticks_left_until_original_wakeup_time = m_wakeup_time - system.uptime; + if (current->m_wakeup_time > system.uptime) { + ASSERT(current->m_was_interrupted_while_blocked); + dword ticks_left_until_original_wakeup_time = current->m_wakeup_time - system.uptime; return ticks_left_until_original_wakeup_time / TICKS_PER_SECOND; } return 0; @@ -1584,9 +1232,9 @@ int Process::sys$sleep(unsigned seconds) if (!seconds) return 0; sleep(seconds * TICKS_PER_SECOND); - if (m_wakeup_time > system.uptime) { - ASSERT(m_was_interrupted_while_blocked); - dword ticks_left_until_original_wakeup_time = m_wakeup_time - system.uptime; + if (current->m_wakeup_time > system.uptime) { + ASSERT(current->m_was_interrupted_while_blocked); + dword ticks_left_until_original_wakeup_time = current->m_wakeup_time - system.uptime; return ticks_left_until_original_wakeup_time / TICKS_PER_SECOND; } return 0; @@ -1657,7 +1305,7 @@ int Process::reap(Process& process) } dbgprintf("reap: %s(%u) {%s}\n", process.name().characters(), process.pid(), to_string(process.state())); - ASSERT(process.state() == Dead); + ASSERT(process.is_dead()); g_processes->remove(&process); delete &process; return exit_status; @@ -1686,7 +1334,7 @@ pid_t Process::sys$waitpid(pid_t waitee, int* wstatus, int options) pid_t reaped_pid = 0; InterruptDisabler disabler; for_each_child([&reaped_pid, &exit_status] (Process& process) { - if (process.state() == Dead) { + if (process.is_dead()) { reaped_pid = process.pid(); exit_status = reap(process); } @@ -1699,7 +1347,7 @@ pid_t Process::sys$waitpid(pid_t waitee, int* wstatus, int options) auto* waitee_process = Process::from_pid(waitee); if (!waitee_process) return -ECHILD; - if (waitee_process->state() == Dead) { + if (waitee_process->is_dead()) { exit_status = reap(*waitee_process); return waitee; } @@ -1707,65 +1355,22 @@ pid_t Process::sys$waitpid(pid_t waitee, int* wstatus, int options) } } - m_waitee_pid = waitee; - block(BlockedWait); + current->m_waitee_pid = waitee; + current->block(Thread::State::BlockedWait); Scheduler::yield(); - if (m_was_interrupted_while_blocked) + if (current->m_was_interrupted_while_blocked) return -EINTR; Process* waitee_process; { InterruptDisabler disabler; // NOTE: If waitee was -1, m_waitee will have been filled in by the scheduler. - waitee_process = Process::from_pid(m_waitee_pid); + waitee_process = Process::from_pid(current->m_waitee_pid); } ASSERT(waitee_process); exit_status = reap(*waitee_process); - return m_waitee_pid; -} - -void Process::unblock() -{ - if (current == this) { - system.nblocked--; - m_state = Process::Running; - return; - } - ASSERT(m_state != Process::Runnable && m_state != Process::Running); - system.nblocked--; - m_state = Process::Runnable; -} - -void Process::snooze_until(Alarm& alarm) -{ - m_snoozing_alarm = &alarm; - block(Process::BlockedSnoozing); - Scheduler::yield(); -} - -void Process::block(Process::State new_state) -{ - if (state() != Process::Running) { - kprintf("Process::block: %s(%u) block(%u/%s) with state=%u/%s\n", name().characters(), pid(), new_state, to_string(new_state), state(), to_string(state())); - } - ASSERT(state() == Process::Running); - system.nblocked++; - m_was_interrupted_while_blocked = false; - set_state(new_state); -} - -void block(Process::State state) -{ - current->block(state); - Scheduler::yield(); + return current->m_waitee_pid; } -void sleep(dword ticks) -{ - ASSERT(current->state() == Process::Running); - current->set_wakeup_time(system.uptime + ticks); - current->block(Process::BlockedSleep); - Scheduler::yield(); -} enum class KernelMemoryCheckResult { NotInsideKernelMemory, @@ -1981,20 +1586,20 @@ int Process::sys$sigprocmask(int how, const sigset_t* set, sigset_t* old_set) if (old_set) { if (!validate_write_typed(old_set)) return -EFAULT; - *old_set = m_signal_mask; + *old_set = current->m_signal_mask; } if (set) { if (!validate_read_typed(set)) return -EFAULT; switch (how) { case SIG_BLOCK: - m_signal_mask &= ~(*set); + current->m_signal_mask &= ~(*set); break; case SIG_UNBLOCK: - m_signal_mask |= *set; + current->m_signal_mask |= *set; break; case SIG_SETMASK: - m_signal_mask = *set; + current->m_signal_mask = *set; break; default: return -EINVAL; @@ -2007,18 +1612,10 @@ int Process::sys$sigpending(sigset_t* set) { if (!validate_write_typed(set)) return -EFAULT; - *set = m_pending_signals; + *set = current->m_pending_signals; return 0; } -void Process::set_default_signal_dispositions() -{ - // FIXME: Set up all the right default actions. See signal(7). - memset(&m_signal_action_data, 0, sizeof(m_signal_action_data)); - m_signal_action_data[SIGCHLD].handler_or_sigaction = LinearAddress((dword)SIG_IGN); - m_signal_action_data[SIGWINCH].handler_or_sigaction = LinearAddress((dword)SIG_IGN); -} - int Process::sys$sigaction(int signum, const sigaction* act, sigaction* old_act) { if (signum < 1 || signum >= 32 || signum == SIGKILL || signum == SIGSTOP) @@ -2026,7 +1623,7 @@ int Process::sys$sigaction(int signum, const sigaction* act, sigaction* old_act) if (!validate_read_typed(act)) return -EFAULT; InterruptDisabler disabler; // FIXME: This should use a narrower lock. Maybe a way to ignore signals temporarily? - auto& action = m_signal_action_data[signum]; + auto& action = current->m_signal_action_data[signum]; if (old_act) { if (!validate_write_typed(old_act)) return -EFAULT; @@ -2119,10 +1716,10 @@ int Process::sys$select(const Syscall::SC_select_params* params) (void)exceptfds; if (timeout) { - m_select_timeout = *timeout; - m_select_has_timeout = true; + current->m_select_timeout = *timeout; + current->m_select_has_timeout = true; } else { - m_select_has_timeout = false; + current->m_select_has_timeout = false; } if (nfds < 0) @@ -2147,22 +1744,22 @@ int Process::sys$select(const Syscall::SC_select_params* params) }; int error = 0; - error = transfer_fds(writefds, m_select_write_fds); + error = transfer_fds(writefds, current->m_select_write_fds); if (error) return error; - error = transfer_fds(readfds, m_select_read_fds); + error = transfer_fds(readfds, current->m_select_read_fds); if (error) return error; - error = transfer_fds(readfds, m_select_exceptional_fds); + error = transfer_fds(readfds, current->m_select_exceptional_fds); if (error) return error; #ifdef DEBUG_IO - dbgprintf("%s<%u> selecting on (read:%u, write:%u), timeout=%p\n", name().characters(), pid(), m_select_read_fds.size(), m_select_write_fds.size(), timeout); + dbgprintf("%s<%u> selecting on (read:%u, write:%u), timeout=%p\n", name().characters(), pid(), current->m_select_read_fds.size(), current->m_select_write_fds.size(), timeout); #endif if (!timeout || (timeout->tv_sec || timeout->tv_usec)) { - block(BlockedSelect); + current->block(Thread::State::BlockedSelect); Scheduler::yield(); } @@ -2171,7 +1768,7 @@ int Process::sys$select(const Syscall::SC_select_params* params) if (readfds) { memset(readfds, 0, sizeof(fd_set)); auto bitmap = Bitmap::wrap((byte*)readfds, FD_SETSIZE); - for (int fd : m_select_read_fds) { + for (int fd : current->m_select_read_fds) { auto* descriptor = file_descriptor(fd); if (!descriptor) continue; @@ -2185,7 +1782,7 @@ int Process::sys$select(const Syscall::SC_select_params* params) if (writefds) { memset(writefds, 0, sizeof(fd_set)); auto bitmap = Bitmap::wrap((byte*)writefds, FD_SETSIZE); - for (int fd : m_select_write_fds) { + for (int fd : current->m_select_write_fds) { auto* descriptor = file_descriptor(fd); if (!descriptor) continue; @@ -2206,17 +1803,17 @@ int Process::sys$poll(pollfd* fds, int nfds, int timeout) if (!validate_read_typed(fds)) return -EFAULT; - m_select_write_fds.clear_with_capacity(); - m_select_read_fds.clear_with_capacity(); + current->m_select_write_fds.clear_with_capacity(); + current->m_select_read_fds.clear_with_capacity(); for (int i = 0; i < nfds; ++i) { if (fds[i].events & POLLIN) - m_select_read_fds.append(fds[i].fd); + current->m_select_read_fds.append(fds[i].fd); if (fds[i].events & POLLOUT) - m_select_write_fds.append(fds[i].fd); + current->m_select_write_fds.append(fds[i].fd); } if (timeout < 0) { - block(BlockedSelect); + current->block(Thread::State::BlockedSelect); Scheduler::yield(); } @@ -2316,6 +1913,7 @@ int Process::sys$chown(const char* pathname, uid_t uid, gid_t gid) void Process::finalize() { ASSERT(current == g_finalizer); + dbgprintf("Finalizing Process %s(%u)\n", m_name.characters(), m_pid); m_fds.clear(); m_tty = nullptr; @@ -2323,7 +1921,8 @@ void Process::finalize() { InterruptDisabler disabler; if (auto* parent_process = Process::from_pid(m_ppid)) { - if (parent_process->m_signal_action_data[SIGCHLD].flags & SA_NOCLDWAIT) { + // FIXME(Thread): What should we do here? Should we look at all threads' signal actions? + if (parent_process->main_thread().m_signal_action_data[SIGCHLD].flags & SA_NOCLDWAIT) { // NOTE: If the parent doesn't care about this process, let it go. m_ppid = 0; } else { @@ -2331,14 +1930,18 @@ void Process::finalize() } } } - m_blocked_socket = nullptr; - - set_state(Dead); } void Process::die() { - set_state(Dying); + { + InterruptDisabler disabler; + for_each_thread([] (Thread& thread) { + if (thread.state() != Thread::State::Dead) + thread.set_state(Thread::State::Dying); + return IterationDecision::Continue; + }); + } if (!Scheduler::is_active()) Scheduler::pick_next_and_switch_now(); @@ -2376,31 +1979,6 @@ size_t Process::amount_shared() const return amount; } -void Process::finalize_dying_processes() -{ - Vector<Process*> dying_processes; - { - InterruptDisabler disabler; - dying_processes.ensure_capacity(system.nprocess); - for (auto* process = g_processes->head(); process; process = process->next()) { - if (process->state() == Process::Dying) - dying_processes.append(process); - } - } - for (auto* process : dying_processes) - process->finalize(); -} - -bool Process::tick() -{ - ++m_ticks; - if (tss().cs & 3) - ++m_ticks_in_user; - else - ++m_ticks_in_kernel; - return --m_ticks_left; -} - int Process::sys$socket(int domain, int type, int protocol) { if (number_of_open_file_descriptors() >= m_max_open_file_descriptors) @@ -2515,19 +2093,6 @@ int Process::sys$connect(int sockfd, const sockaddr* address, socklen_t address_ return 0; } -KResult Process::wait_for_connect(Socket& socket) -{ - if (socket.is_connected()) - return KSuccess; - m_blocked_socket = socket; - block(BlockedConnect); - Scheduler::yield(); - m_blocked_socket = nullptr; - if (!socket.is_connected()) - return KResult(-ECONNREFUSED); - return KSuccess; -} - ssize_t Process::sys$sendto(const Syscall::SC_sendto_params* params) { if (!validate_read_typed(params)) @@ -2850,34 +2415,6 @@ int Process::sys$get_shared_buffer_size(int shared_buffer_id) return shared_buffer.size(); } -const char* to_string(Process::State state) -{ - switch (state) { - case Process::Invalid: return "Invalid"; - case Process::Runnable: return "Runnable"; - case Process::Running: return "Running"; - case Process::Dying: return "Dying"; - case Process::Dead: return "Dead"; - case Process::Stopped: return "Stopped"; - case Process::Skip1SchedulerPass: return "Skip1"; - case Process::Skip0SchedulerPasses: return "Skip0"; - case Process::BlockedSleep: return "Sleep"; - case Process::BlockedWait: return "Wait"; - case Process::BlockedRead: return "Read"; - case Process::BlockedWrite: return "Write"; - case Process::BlockedSignal: return "Signal"; - case Process::BlockedSelect: return "Select"; - case Process::BlockedLurking: return "Lurking"; - case Process::BlockedConnect: return "Connect"; - case Process::BlockedReceive: return "Receive"; - case Process::BeingInspected: return "Inspect"; - case Process::BlockedSnoozing: return "Snoozing"; - } - kprintf("to_string(Process::State): Invalid state: %u\n", state); - ASSERT_NOT_REACHED(); - return nullptr; -} - const char* to_string(Process::Priority priority) { switch (priority) { @@ -2889,3 +2426,29 @@ const char* to_string(Process::Priority priority) ASSERT_NOT_REACHED(); return nullptr; } + +void Process::terminate_due_to_signal(byte signal) +{ + ASSERT_INTERRUPTS_DISABLED(); + ASSERT(signal < 32); + dbgprintf("terminate_due_to_signal %s(%u) <- %u\n", name().characters(), pid(), signal); + m_termination_status = 0; + m_termination_signal = signal; + die(); +} + +void Process::send_signal(byte signal, Process* sender) +{ + // FIXME(Thread): Find the appropriate thread to deliver the signal to. + main_thread().send_signal(signal, sender); +} + +int Process::thread_count() const +{ + int count = 0; + for_each_thread([&count] (auto&) { + ++count; + return IterationDecision::Continue; + }); + return count; +} diff --git a/Kernel/Process.h b/Kernel/Process.h index 75f5a86637..20084c4e73 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -1,8 +1,6 @@ #pragma once #include "types.h" -#include "TSS.h" -#include "i386.h" #include "TTY.h" #include "Syscall.h" #include <Kernel/VirtualFileSystem.h> @@ -12,9 +10,9 @@ #include <AK/Vector.h> #include <AK/WeakPtr.h> #include <AK/Weakable.h> +#include <Kernel/Thread.h> #include <Kernel/Lock.h> -class Alarm; class FileDescriptor; class PageDirectory; class Region; @@ -31,26 +29,11 @@ struct CoolGlobals { extern CoolGlobals* g_cool_globals; #endif -enum class ShouldUnblockProcess { No = 0, Yes }; - -struct SignalActionData { - LinearAddress handler_or_sigaction; - dword mask { 0 }; - int flags { 0 }; - LinearAddress restorer; -}; - -struct DisplayInfo { - unsigned width; - unsigned height; - unsigned bpp; - unsigned pitch; -}; - void kgettimeofday(timeval&); class Process : public InlineLinkedListNode<Process>, public Weakable<Process> { friend class InlineLinkedListNode<Process>; + friend class Thread; public: static Process* create_kernel_process(String&& name, void (*entry)()); static Process* create_user_process(const String& path, uid_t, gid_t, pid_t ppid, int& error, Vector<String>&& arguments = Vector<String>(), Vector<String>&& environment = Vector<String>(), TTY* = nullptr); @@ -58,29 +41,6 @@ public: static Vector<pid_t> all_pids(); static Vector<Process*> all_processes(); - static void finalize_dying_processes(); - - enum State { - Invalid = 0, - Runnable, - Running, - Skip1SchedulerPass, - Skip0SchedulerPasses, - Dying, - Dead, - Stopped, - BeingInspected, - BlockedLurking, - BlockedSleep, - BlockedWait, - BlockedRead, - BlockedWrite, - BlockedSignal, - BlockedSelect, - BlockedConnect, - BlockedReceive, - BlockedSnoozing, - }; enum Priority { LowPriority, @@ -93,19 +53,20 @@ public: Ring3 = 3, }; + // FIXME(Thread): Is this really how this should work? + bool is_dead() const { return main_thread().state() == Thread::State::Dead; } + + Thread::State state() const { return main_thread().state(); } + + Thread& main_thread() { return *m_main_thread; } + const Thread& main_thread() const { return *m_main_thread; } + bool is_ring0() const { return m_ring == Ring0; } bool is_ring3() const { return m_ring == Ring3; } - bool is_stopped() const { return m_state == Stopped; } - bool is_blocked() const - { - return m_state == BlockedSleep || m_state == BlockedWait || m_state == BlockedRead || m_state == BlockedWrite || m_state == BlockedSignal || m_state == BlockedSelect; - } PageDirectory& page_directory() { return *m_page_directory; } const PageDirectory& page_directory() const { return *m_page_directory; } - bool in_kernel() const { return (m_tss.cs & 0x03) == 0; } - static Process* from_pid(pid_t); void set_priority(Priority p) { m_priority = p; } @@ -115,10 +76,6 @@ public: pid_t pid() const { return m_pid; } pid_t sid() const { return m_sid; } pid_t pgid() const { return m_pgid; } - dword ticks() const { return m_ticks; } - word selector() const { return m_far_ptr.selector; } - TSS32& tss() { return m_tss; } - State state() const { return m_state; } uid_t uid() const { return m_uid; } gid_t gid() const { return m_gid; } const HashTable<gid_t>& gids() const { return m_gids; } @@ -130,34 +87,14 @@ public: bool in_group(gid_t) const; - const FarPtr& far_ptr() const { return m_far_ptr; } - FileDescriptor* file_descriptor(int fd); const FileDescriptor* file_descriptor(int fd) const; - void block(Process::State); - void unblock(); - - void set_wakeup_time(dword t) { m_wakeup_time = t; } - dword wakeup_time() const { return m_wakeup_time; } - - void snooze_until(Alarm&); - template<typename Callback> static void for_each(Callback); template<typename Callback> static void for_each_in_pgrp(pid_t, Callback); - template<typename Callback> static void for_each_in_state(State, Callback); - template<typename Callback> static void for_each_living(Callback); template<typename Callback> void for_each_child(Callback); + template<typename Callback> void for_each_thread(Callback) const; - bool tick(); - void set_ticks_left(dword t) { m_ticks_left = t; } - dword ticks_left() const { return m_ticks_left; } - - dword kernel_stack_base() const { return (dword)m_kernel_stack; }; - dword kernel_stack_for_signal_handler_base() const { return (dword)m_kernel_stack_for_signal_handler; }; - - void set_selector(word s) { m_far_ptr.selector = s; } - void set_state(State s) { m_state = s; } void die(); void finalize(); @@ -182,7 +119,6 @@ public: int sys$stat(const char*, stat*); int sys$lseek(int fd, off_t, int whence); int sys$kill(pid_t pid, int sig); - int sys$geterror() { return m_error; } [[noreturn]] void sys$exit(int status); [[noreturn]] void sys$sigreturn(); pid_t sys$waitpid(pid_t, int* wstatus, int options); @@ -249,8 +185,6 @@ public: int sys$seal_shared_buffer(int shared_buffer_id); int sys$get_shared_buffer_size(int shared_buffer_id); - KResult wait_for_connect(Socket&); - static void initialize(); [[noreturn]] void crash(); @@ -263,21 +197,12 @@ public: const Vector<Retained<Region>>& regions() const { return m_regions; } void dump_regions(); - void did_schedule() { ++m_times_scheduled; } - dword times_scheduled() const { return m_times_scheduled; } - dword m_ticks_in_user { 0 }; dword m_ticks_in_kernel { 0 }; dword m_ticks_in_user_for_dead_children { 0 }; dword m_ticks_in_kernel_for_dead_children { 0 }; - pid_t waitee_pid() const { return m_waitee_pid; } - - dword frame_ptr() const { return m_tss.ebp; } - dword stack_ptr() const { return m_tss.esp; } - dword stack_top() const { return m_tss.ss == 0x10 ? m_stack_top0 : m_stack_top3; } - bool validate_read_from_kernel(LinearAddress) const; bool validate_read(const void*, ssize_t) const; @@ -292,13 +217,6 @@ public: int number_of_open_file_descriptors() const; int max_open_file_descriptors() const { return m_max_open_file_descriptors; } - void send_signal(byte signal, Process* sender); - - ShouldUnblockProcess dispatch_one_pending_signal(); - ShouldUnblockProcess dispatch_signal(byte signal); - bool has_unmasked_pending_signals() const; - void terminate_due_to_signal(byte signal); - size_t amount_virtual() const; size_t amount_resident() const; size_t amount_shared() const; @@ -308,16 +226,18 @@ public: bool is_superuser() const { return m_euid == 0; } - FPUState& fpu_state() { return m_fpu_state; } - bool has_used_fpu() const { return m_has_used_fpu; } - void set_has_used_fpu(bool b) { m_has_used_fpu = b; } - Region* allocate_region_with_vmo(LinearAddress, size_t, Retained<VMObject>&&, size_t offset_in_vmo, String&& name, bool is_readable, bool is_writable); Region* allocate_file_backed_region(LinearAddress, size_t, RetainPtr<Inode>&&, String&& name, bool is_readable, bool is_writable); Region* allocate_region(LinearAddress, size_t, String&& name, bool is_readable = true, bool is_writable = true, bool commit = true); bool deallocate_region(Region& region); - void set_blocked_socket(Socket* socket) { m_blocked_socket = socket; } + void set_being_inspected(bool b) { m_being_inspected = b; } + bool is_being_inspected() const { return m_being_inspected; } + + void terminate_due_to_signal(byte signal); + void send_signal(byte, Process* sender); + + int thread_count() const; private: friend class MemoryManager; @@ -327,22 +247,21 @@ private: Process(String&& name, uid_t, gid_t, pid_t ppid, RingLevel, RetainPtr<Inode>&& cwd = nullptr, RetainPtr<Inode>&& executable = nullptr, TTY* = nullptr, Process* fork_parent = nullptr); int do_exec(String path, Vector<String> arguments, Vector<String> environment); - void push_value_on_stack(dword); - void make_userspace_stack(Vector<String> arguments, Vector<String> environment); int alloc_fd(); - void set_default_signal_dispositions(); void disown_all_shared_buffers(); void create_signal_trampolines_if_needed(); + Thread* m_main_thread { nullptr }; + RetainPtr<PageDirectory> m_page_directory; Process* m_prev { nullptr }; Process* m_next { nullptr }; String m_name; - void (*m_entry)() { nullptr }; + pid_t m_pid { 0 }; uid_t m_uid { 0 }; gid_t m_gid { 0 }; @@ -350,17 +269,9 @@ private: gid_t m_egid { 0 }; pid_t m_sid { 0 }; pid_t m_pgid { 0 }; - dword m_ticks { 0 }; - dword m_ticks_left { 0 }; - dword m_stack_top0 { 0 }; - dword m_stack_top3 { 0 }; - FarPtr m_far_ptr; - State m_state { Invalid }; + Priority m_priority { NormalPriority }; - dword m_wakeup_time { 0 }; - TSS32 m_tss; - TSS32 m_tss_to_resume_kernel; - FPUState m_fpu_state; + struct FileDescriptorAndFlags { operator bool() const { return !!descriptor; } void clear() { descriptor = nullptr; flags = 0; } @@ -370,23 +281,8 @@ private: }; Vector<FileDescriptorAndFlags> m_fds; RingLevel m_ring { Ring0 }; - int m_error { 0 }; - void* m_kernel_stack { nullptr }; - void* m_kernel_stack_for_signal_handler { nullptr }; - dword m_times_scheduled { 0 }; - pid_t m_waitee_pid { -1 }; - int m_blocked_fd { -1 }; - Vector<int> m_select_read_fds; - Vector<int> m_select_write_fds; - Vector<int> m_select_exceptional_fds; - timeval m_select_timeout; - bool m_select_has_timeout { false }; + int m_max_open_file_descriptors { 16 }; - SignalActionData m_signal_action_data[32]; - dword m_pending_signals { 0 }; - dword m_signal_mask { 0 }; - RetainPtr<Socket> m_blocked_socket; - Alarm* m_snoozing_alarm { nullptr }; byte m_termination_status { 0 }; byte m_termination_signal { 0 }; @@ -409,34 +305,28 @@ private: pid_t m_ppid { 0 }; mode_t m_umask { 022 }; - bool m_was_interrupted_while_blocked { false }; - static void notify_waiters(pid_t waitee, int exit_status, int signal); HashTable<gid_t> m_gids; - Region* m_signal_stack_user_region { nullptr }; + bool m_being_inspected { false }; - RetainPtr<Region> m_display_framebuffer_region; - - bool m_has_used_fpu { false }; + int m_next_tid { 0 }; }; -extern Process* current; - class ProcessInspectionHandle { public: ProcessInspectionHandle(Process& process) : m_process(process) - , m_original_state(process.state()) { - if (&process != current) - m_process.set_state(Process::BeingInspected); + if (&process != ¤t->process()) { + ASSERT(!m_process.is_being_inspected()); + m_process.set_being_inspected(true); + } } - ~ProcessInspectionHandle() { - m_process.set_state(m_original_state); + m_process.set_being_inspected(false); } Process& process() { return m_process; } @@ -454,15 +344,10 @@ public: Process& operator*() { return m_process; } private: Process& m_process; - Process::State m_original_state { Process::Invalid }; }; -extern const char* to_string(Process::State); extern const char* to_string(Process::Priority); -extern void block(Process::State); -extern void sleep(dword ticks); - extern InlineLinkedList<Process>* g_processes; template<typename Callback> @@ -493,39 +378,30 @@ inline void Process::for_each_child(Callback callback) } template<typename Callback> -inline void Process::for_each_in_pgrp(pid_t pgid, Callback callback) +inline void Process::for_each_thread(Callback callback) const { - ASSERT_INTERRUPTS_DISABLED(); - for (auto* process = g_processes->head(); process;) { - auto* next_process = process->next(); - if (process->pgid() == pgid) { - if (!callback(*process)) + InterruptDisabler disabler; + pid_t my_pid = pid(); + for (auto* thread = g_threads->head(); thread;) { + auto* next_thread = thread->next(); + if (thread->pid() == my_pid) { + if (callback(*thread) == IterationDecision::Abort) break; } - process = next_process; - } -} - -template<typename Callback> -inline void Process::for_each_in_state(State state, Callback callback) -{ - ASSERT_INTERRUPTS_DISABLED(); - for (auto* process = g_processes->head(); process;) { - auto* next_process = process->next(); - if (process->state() == state) - callback(*process); - process = next_process; + thread = next_thread; } } template<typename Callback> -inline void Process::for_each_living(Callback callback) +inline void Process::for_each_in_pgrp(pid_t pgid, Callback callback) { ASSERT_INTERRUPTS_DISABLED(); for (auto* process = g_processes->head(); process;) { auto* next_process = process->next(); - if (process->state() != Process::Dead && process->state() != Process::Dying) - callback(*process); + if (process->pgid() == pgid) { + if (!callback(*process)) + break; + } process = next_process; } } @@ -544,3 +420,8 @@ inline bool InodeMetadata::may_execute(Process& process) const { return may_execute(process.euid(), process.gids()); } + +inline int Thread::pid() const +{ + return m_process.pid(); +} diff --git a/Kernel/Scheduler.cpp b/Kernel/Scheduler.cpp index 84cd0fcd04..0bdcb544c2 100644 --- a/Kernel/Scheduler.cpp +++ b/Kernel/Scheduler.cpp @@ -23,9 +23,9 @@ static dword time_slice_for(Process::Priority priority) ASSERT_NOT_REACHED(); } -Process* current; -Process* g_last_fpu_process; -Process* g_finalizer; +Thread* current; +Thread* g_last_fpu_thread; +Thread* g_finalizer; static Process* s_colonel_process; struct TaskRedirectionData { @@ -52,138 +52,144 @@ bool Scheduler::pick_next() if (!current) { // XXX: The first ever context_switch() goes to the idle process. // This to setup a reliable place we can return to. - return context_switch(*s_colonel_process); + return context_switch(s_colonel_process->main_thread()); } auto now_sec = RTC::now(); auto now_usec = (suseconds_t)((PIT::ticks_since_boot() % 1000) * 1000); - // Check and unblock processes whose wait conditions have been met. - Process::for_each([&] (Process& process) { - if (process.state() == Process::BlockedSleep) { - if (process.wakeup_time() <= system.uptime) - process.unblock(); - return true; + // Check and unblock threads whose wait conditions have been met. + Thread::for_each([&] (Thread& thread) { + auto& process = thread.process(); + +// dbgprintf("pick_next, checking on %s(%u:%u) in state %s\n", process.name().impl()->characters(), process.pid(), thread.tid(), to_string(thread.state())); + + if (thread.state() == Thread::BlockedSleep) { + if (thread.wakeup_time() <= system.uptime) + thread.unblock(); + return IterationDecision::Continue; } - if (process.state() == Process::BlockedWait) { - process.for_each_child([&process] (Process& child) { - if (child.state() != Process::Dead) + if (thread.state() == Thread::BlockedWait) { + process.for_each_child([&] (Process& child) { + if (child.state() != Thread::Dead) return true; - if (process.waitee_pid() == -1 || process.waitee_pid() == child.pid()) { - process.m_waitee_pid = child.pid(); - process.unblock(); + if (thread.waitee_pid() == -1 || thread.waitee_pid() == child.pid()) { + thread.m_waitee_pid = child.pid(); + thread.unblock(); return false; } return true; }); - return true; + return IterationDecision::Continue; } - if (process.state() == Process::BlockedRead) { - ASSERT(process.m_blocked_fd != -1); + if (thread.state() == Thread::BlockedRead) { + ASSERT(thread.m_blocked_fd != -1); // FIXME: Block until the amount of data wanted is available. - if (process.m_fds[process.m_blocked_fd].descriptor->can_read(process)) - process.unblock(); - return true; + if (process.m_fds[thread.m_blocked_fd].descriptor->can_read(process)) + thread.unblock(); + return IterationDecision::Continue; } - if (process.state() == Process::BlockedWrite) { - ASSERT(process.m_blocked_fd != -1); - if (process.m_fds[process.m_blocked_fd].descriptor->can_write(process)) - process.unblock(); - return true; + if (thread.state() == Thread::BlockedWrite) { + ASSERT(thread.m_blocked_fd != -1); + if (process.m_fds[thread.m_blocked_fd].descriptor->can_write(process)) + thread.unblock(); + return IterationDecision::Continue; } - if (process.state() == Process::BlockedConnect) { - ASSERT(process.m_blocked_socket); - if (process.m_blocked_socket->is_connected()) - process.unblock(); - return true; + if (thread.state() == Thread::BlockedConnect) { + ASSERT(thread.m_blocked_socket); + if (thread.m_blocked_socket->is_connected()) + thread.unblock(); + return IterationDecision::Continue; } - if (process.state() == Process::BlockedReceive) { - ASSERT(process.m_blocked_socket); - auto& socket = *process.m_blocked_socket; + if (thread.state() == Thread::BlockedReceive) { + ASSERT(thread.m_blocked_socket); + auto& socket = *thread.m_blocked_socket; // FIXME: Block until the amount of data wanted is available. bool timed_out = now_sec > socket.receive_deadline().tv_sec || (now_sec == socket.receive_deadline().tv_sec && now_usec >= socket.receive_deadline().tv_usec); if (timed_out || socket.can_read(SocketRole::None)) { - process.unblock(); - process.m_blocked_socket = nullptr; - return true; + thread.unblock(); + thread.m_blocked_socket = nullptr; + return IterationDecision::Continue; } - return true; + return IterationDecision::Continue; } - if (process.state() == Process::BlockedSelect) { - if (process.m_select_has_timeout) { - if (now_sec > process.m_select_timeout.tv_sec || (now_sec == process.m_select_timeout.tv_sec && now_usec >= process.m_select_timeout.tv_usec)) { - process.unblock(); - return true; + if (thread.state() == Thread::BlockedSelect) { + if (thread.m_select_has_timeout) { + if (now_sec > thread.m_select_timeout.tv_sec || (now_sec == thread.m_select_timeout.tv_sec && now_usec >= thread.m_select_timeout.tv_usec)) { + thread.unblock(); + return IterationDecision::Continue; } } - for (int fd : process.m_select_read_fds) { + for (int fd : thread.m_select_read_fds) { if (process.m_fds[fd].descriptor->can_read(process)) { - process.unblock(); - return true; + thread.unblock(); + return IterationDecision::Continue; } } - for (int fd : process.m_select_write_fds) { + for (int fd : thread.m_select_write_fds) { if (process.m_fds[fd].descriptor->can_write(process)) { - process.unblock(); - return true; + thread.unblock(); + return IterationDecision::Continue; } } - return true; + return IterationDecision::Continue; } - if (process.state() == Process::BlockedSnoozing) { - if (process.m_snoozing_alarm->is_ringing()) { - process.m_snoozing_alarm = nullptr; - process.unblock(); + if (thread.state() == Thread::BlockedSnoozing) { + if (thread.m_snoozing_alarm->is_ringing()) { + thread.m_snoozing_alarm = nullptr; + thread.unblock(); } - return true; + return IterationDecision::Continue; } - if (process.state() == Process::Skip1SchedulerPass) { - process.set_state(Process::Skip0SchedulerPasses); - return true; + if (thread.state() == Thread::Skip1SchedulerPass) { + thread.set_state(Thread::Skip0SchedulerPasses); + return IterationDecision::Continue; } - if (process.state() == Process::Skip0SchedulerPasses) { - process.set_state(Process::Runnable); - return true; + if (thread.state() == Thread::Skip0SchedulerPasses) { + thread.set_state(Thread::Runnable); + return IterationDecision::Continue; } - if (process.state() == Process::Dead) { - if (current != &process && (!process.ppid() || !Process::from_pid(process.ppid()))) { + if (thread.state() == Thread::Dying) { + ASSERT(g_finalizer); + if (g_finalizer->state() == Thread::BlockedLurking) + g_finalizer->unblock(); + return IterationDecision::Continue; + } + + return IterationDecision::Continue; + }); + + Process::for_each([&] (Process& process) { + if (process.is_dead()) { + if (current != &process.main_thread() && (!process.ppid() || !Process::from_pid(process.ppid()))) { auto name = process.name(); auto pid = process.pid(); auto exit_status = Process::reap(process); dbgprintf("reaped unparented process %s(%u), exit status: %u\n", name.characters(), pid, exit_status); } - return true; } - - if (process.state() == Process::Dying) { - ASSERT(g_finalizer); - if (g_finalizer->state() == Process::BlockedLurking) - g_finalizer->unblock(); - return true; - } - return true; }); // Dispatch any pending signals. // FIXME: Do we really need this to be a separate pass over the process list? - Process::for_each_living([] (auto& process) { - if (!process.has_unmasked_pending_signals()) + Thread::for_each_living([] (Thread& thread) { + if (!thread.has_unmasked_pending_signals()) return true; // FIXME: It would be nice if the Scheduler didn't have to worry about who is "current" // For now, avoid dispatching signals to "current" and do it in a scheduling pass // while some other process is interrupted. Otherwise a mess will be made. - if (&process == current) + if (&thread == current) return true; // We know how to interrupt blocked processes, but if they are just executing // at some random point in the kernel, let them continue. They'll be in userspace @@ -191,60 +197,61 @@ bool Scheduler::pick_next() // FIXME: Maybe we could check when returning from a syscall if there's a pending // signal and dispatch it then and there? Would that be doable without the // syscall effectively being "interrupted" despite having completed? - if (process.in_kernel() && !process.is_blocked() && !process.is_stopped()) + if (thread.in_kernel() && !thread.is_blocked() && !thread.is_stopped()) return true; // NOTE: dispatch_one_pending_signal() may unblock the process. - bool was_blocked = process.is_blocked(); - if (process.dispatch_one_pending_signal() == ShouldUnblockProcess::No) + bool was_blocked = thread.is_blocked(); + if (thread.dispatch_one_pending_signal() == ShouldUnblockThread::No) return true; if (was_blocked) { - dbgprintf("Unblock %s(%u) due to signal\n", process.name().characters(), process.pid()); - process.m_was_interrupted_while_blocked = true; - process.unblock(); + dbgprintf("Unblock %s(%u) due to signal\n", thread.process().name().characters(), thread.pid()); + thread.m_was_interrupted_while_blocked = true; + thread.unblock(); } return true; }); #ifdef SCHEDULER_DEBUG dbgprintf("Scheduler choices:\n"); - for (auto* process = g_processes->head(); process; process = process->next()) { - //if (process->state() == Process::BlockedWait || process->state() == Process::BlockedSleep) + for (auto* thread = g_threads->head(); thread; thread = thread->next()) { + //if (process->state() == Thread::BlockedWait || process->state() == Thread::BlockedSleep) // continue; - dbgprintf("[K%x] % 12s %s(%u) @ %w:%x\n", process, to_string(process->state()), process->name().characters(), process->pid(), process->tss().cs, process->tss().eip); + auto* process = &thread->process(); + dbgprintf("[K%x] % 12s %s(%u:%u) @ %w:%x\n", process, to_string(thread->state()), process->name().characters(), process->pid(), thread->tid(), thread->tss().cs, thread->tss().eip); } #endif - auto* previous_head = g_processes->head(); + auto* previous_head = g_threads->head(); for (;;) { // Move head to tail. - g_processes->append(g_processes->remove_head()); - auto* process = g_processes->head(); + g_threads->append(g_threads->remove_head()); + auto* thread = g_threads->head(); - if (process->state() == Process::Runnable || process->state() == Process::Running) { + if (!thread->process().is_being_inspected() && (thread->state() == Thread::Runnable || thread->state() == Thread::Running)) { #ifdef SCHEDULER_DEBUG - kprintf("switch to %s(%u) @ %w:%x\n", process->name().characters(), process->pid(), process->tss().cs, process->tss().eip); + kprintf("switch to %s(%u:%u) @ %w:%x\n", thread->process().name().characters(), thread->process().pid(), thread->tid(), thread->tss().cs, thread->tss().eip); #endif - return context_switch(*process); + return context_switch(*thread); } - if (process == previous_head) { + if (thread == previous_head) { // Back at process_head, nothing wants to run. Send in the colonel! - return context_switch(*s_colonel_process); + return context_switch(s_colonel_process->main_thread()); } } } -bool Scheduler::donate_to(Process* beneficiary, const char* reason) +bool Scheduler::donate_to(Thread* beneficiary, const char* reason) { (void)reason; unsigned ticks_left = current->ticks_left(); - if (!beneficiary || beneficiary->state() != Process::Runnable || ticks_left <= 1) { + if (!beneficiary || beneficiary->state() != Thread::Runnable || ticks_left <= 1) { return yield(); } - unsigned ticks_to_donate = min(ticks_left - 1, time_slice_for(beneficiary->priority())); + unsigned ticks_to_donate = min(ticks_left - 1, time_slice_for(beneficiary->process().priority())); #ifdef SCHEDULER_DEBUG - dbgprintf("%s(%u) donating %u ticks to %s(%u), reason=%s\n", current->name().characters(), current->pid(), ticks_to_donate, beneficiary->name().characters(), beneficiary->pid(), reason); + dbgprintf("%s(%u:%u) donating %u ticks to %s(%u:%u), reason=%s\n", current->process().name().characters(), current->pid(), current->tid(), ticks_to_donate, beneficiary->process().name().characters(), beneficiary->pid(), beneficiary->tid(), reason); #endif context_switch(*beneficiary); beneficiary->set_ticks_left(ticks_to_donate); @@ -256,12 +263,12 @@ bool Scheduler::yield() { InterruptDisabler disabler; ASSERT(current); - //dbgprintf("%s<%u> yield()\n", current->name().characters(), current->pid()); +// dbgprintf("%s(%u:%u) yield()\n", current->process().name().characters(), current->pid(), current->tid()); if (!pick_next()) return 1; - //dbgprintf("yield() jumping to new process: %x (%s)\n", current->far_ptr().selector, current->name().characters()); +// dbgprintf("yield() jumping to new process: sel=%x, %s(%u:%u)\n", current->far_ptr().selector, current->process().name().characters(), current->pid(), current->tid()); switch_now(); return 0; } @@ -284,39 +291,44 @@ void Scheduler::switch_now() ); } -bool Scheduler::context_switch(Process& process) +bool Scheduler::context_switch(Thread& thread) { - process.set_ticks_left(time_slice_for(process.priority())); - process.did_schedule(); + thread.set_ticks_left(time_slice_for(thread.process().priority())); + + // FIXME(Thread): This is such a hack. + if (&thread == &s_colonel_process->main_thread()) + thread.set_ticks_left(1); + + thread.did_schedule(); - if (current == &process) + if (current == &thread) return false; if (current) { // If the last process hasn't blocked (still marked as running), // mark it as runnable for the next round. - if (current->state() == Process::Running) - current->set_state(Process::Runnable); + if (current->state() == Thread::Running) + current->set_state(Thread::Runnable); #ifdef LOG_EVERY_CONTEXT_SWITCH - dbgprintf("Scheduler: %s(%u) -> %s(%u) %w:%x\n", - current->name().characters(), current->pid(), - process.name().characters(), process.pid(), - process.tss().cs, process.tss().eip); + dbgprintf("Scheduler: %s(%u:%u) -> %s(%u:%u) %w:%x\n", + current->process().name().characters(), current->process().pid(), current->tid(), + thread.process().name().characters(), thread.process().pid(), thread.tid(), + thread.tss().cs, thread.tss().eip); #endif } - current = &process; - process.set_state(Process::Running); + current = &thread; + thread.set_state(Thread::Running); #ifdef COOL_GLOBALS - g_cool_globals->current_pid = process.pid(); + g_cool_globals->current_pid = thread.process().pid(); #endif - if (!process.selector()) { - process.set_selector(gdt_alloc_entry()); - auto& descriptor = get_gdt_entry(process.selector()); - descriptor.set_base(&process.tss()); + if (!thread.selector()) { + thread.set_selector(gdt_alloc_entry()); + auto& descriptor = get_gdt_entry(thread.selector()); + descriptor.set_base(&thread.tss()); descriptor.set_limit(0xffff); descriptor.dpl = 0; descriptor.segment_present = 1; @@ -326,7 +338,7 @@ bool Scheduler::context_switch(Process& process) descriptor.descriptor_type = 0; } - auto& descriptor = get_gdt_entry(process.selector()); + auto& descriptor = get_gdt_entry(thread.selector()); descriptor.type = 11; // Busy TSS flush_gdt(); return true; @@ -355,12 +367,12 @@ void Scheduler::prepare_for_iret_to_new_process() load_task_register(s_redirection.selector); } -void Scheduler::prepare_to_modify_tss(Process& process) +void Scheduler::prepare_to_modify_tss(Thread& thread) { // This ensures that a currently running process modifying its own TSS // in order to yield() and end up somewhere else doesn't just end up // right after the yield(). - if (current == &process) + if (current == &thread) load_task_register(s_redirection.selector); } diff --git a/Kernel/Scheduler.h b/Kernel/Scheduler.h index 918b553b3a..04c80e6bd8 100644 --- a/Kernel/Scheduler.h +++ b/Kernel/Scheduler.h @@ -3,11 +3,12 @@ #include <AK/Assertions.h> class Process; +class Thread; struct RegisterDump; -extern Process* current; -extern Process* g_last_fpu_process; -extern Process* g_finalizer; +extern Thread* current; +extern Thread* g_last_fpu_thread; +extern Thread* g_finalizer; class Scheduler { public: @@ -17,9 +18,9 @@ public: static void pick_next_and_switch_now(); static void switch_now(); static bool yield(); - static bool donate_to(Process*, const char* reason); - static bool context_switch(Process&); - static void prepare_to_modify_tss(Process&); + static bool donate_to(Thread*, const char* reason); + static bool context_switch(Thread&); + static void prepare_to_modify_tss(Thread&); static Process* colonel(); static bool is_active(); private: diff --git a/Kernel/SlavePTY.cpp b/Kernel/SlavePTY.cpp index ddbbcc3176..93d2a0b652 100644 --- a/Kernel/SlavePTY.cpp +++ b/Kernel/SlavePTY.cpp @@ -8,8 +8,8 @@ SlavePTY::SlavePTY(MasterPTY& master, unsigned index) , m_master(master) , m_index(index) { - set_uid(current->uid()); - set_gid(current->gid()); + set_uid(current->process().uid()); + set_gid(current->process().gid()); DevPtsFS::the().register_slave_pty(*this); set_size(80, 25); } diff --git a/Kernel/StdLib.cpp b/Kernel/StdLib.cpp index ed18781b72..20c07f9341 100644 --- a/Kernel/StdLib.cpp +++ b/Kernel/StdLib.cpp @@ -35,6 +35,18 @@ void* memcpy(void* dest_ptr, const void* src_ptr, size_t n) return dest_ptr; } +void* memmove(void* dest, const void* src, size_t n) +{ + if (dest < src) + return memcpy(dest, src, n); + + byte *pd = (byte*)dest; + const byte *ps = (const byte*)src; + for (pd += n, ps += n; n--;) + *--pd = *--ps; + return dest; +} + char* strcpy(char* dest, const char *src) { auto* dest_ptr = dest; diff --git a/Kernel/StdLib.h b/Kernel/StdLib.h index 3af0ba98d7..88a40eed6e 100644 --- a/Kernel/StdLib.h +++ b/Kernel/StdLib.h @@ -15,6 +15,7 @@ void* memset(void*, int, size_t); char *strdup(const char*); int memcmp(const void*, const void*, size_t); char* strrchr(const char* str, int ch); +void* memmove(void* dest, const void* src, size_t n); inline word ntohs(word w) { return (w & 0xff) << 8 | ((w >> 8) & 0xff); } inline word htons(word w) { return (w & 0xff) << 8 | ((w >> 8) & 0xff); } diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index b952aa598d..ffb59b5386 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -61,182 +61,182 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2, Console::the().put_char(arg1 & 0xff); break; case Syscall::SC_sleep: - return current->sys$sleep((unsigned)arg1); + return current->process().sys$sleep((unsigned)arg1); case Syscall::SC_usleep: - return current->sys$usleep((unsigned)arg1); + return current->process().sys$usleep((unsigned)arg1); case Syscall::SC_gettimeofday: - return current->sys$gettimeofday((timeval*)arg1); + return current->process().sys$gettimeofday((timeval*)arg1); case Syscall::SC_get_dir_entries: - return current->sys$get_dir_entries((int)arg1, (void*)arg2, (size_t)arg3); + return current->process().sys$get_dir_entries((int)arg1, (void*)arg2, (size_t)arg3); case Syscall::SC_lstat: - return current->sys$lstat((const char*)arg1, (stat*)arg2); + return current->process().sys$lstat((const char*)arg1, (stat*)arg2); case Syscall::SC_stat: - return current->sys$stat((const char*)arg1, (stat*)arg2); + return current->process().sys$stat((const char*)arg1, (stat*)arg2); case Syscall::SC_getcwd: - return current->sys$getcwd((char*)arg1, (size_t)arg2); + return current->process().sys$getcwd((char*)arg1, (size_t)arg2); case Syscall::SC_open: - return current->sys$open((const char*)arg1, (int)arg2, (mode_t)arg3); + return current->process().sys$open((const char*)arg1, (int)arg2, (mode_t)arg3); case Syscall::SC_write: - return current->sys$write((int)arg1, (const byte*)arg2, (ssize_t)arg3); + return current->process().sys$write((int)arg1, (const byte*)arg2, (ssize_t)arg3); case Syscall::SC_close: - return current->sys$close((int)arg1); + return current->process().sys$close((int)arg1); case Syscall::SC_read: - return current->sys$read((int)arg1, (byte*)arg2, (ssize_t)arg3); + return current->process().sys$read((int)arg1, (byte*)arg2, (ssize_t)arg3); case Syscall::SC_lseek: - return current->sys$lseek((int)arg1, (off_t)arg2, (int)arg3); + return current->process().sys$lseek((int)arg1, (off_t)arg2, (int)arg3); case Syscall::SC_kill: - return current->sys$kill((pid_t)arg1, (int)arg2); + return current->process().sys$kill((pid_t)arg1, (int)arg2); case Syscall::SC_getuid: - return current->sys$getuid(); + return current->process().sys$getuid(); case Syscall::SC_getgid: - return current->sys$getgid(); + return current->process().sys$getgid(); case Syscall::SC_getpid: - return current->sys$getpid(); + return current->process().sys$getpid(); case Syscall::SC_getppid: - return current->sys$getppid(); + return current->process().sys$getppid(); case Syscall::SC_waitpid: - return current->sys$waitpid((pid_t)arg1, (int*)arg2, (int)arg3); + return current->process().sys$waitpid((pid_t)arg1, (int*)arg2, (int)arg3); case Syscall::SC_mmap: - return (dword)current->sys$mmap((const SC_mmap_params*)arg1); + return (dword)current->process().sys$mmap((const SC_mmap_params*)arg1); case Syscall::SC_select: - return current->sys$select((const SC_select_params*)arg1); + return current->process().sys$select((const SC_select_params*)arg1); case Syscall::SC_poll: - return current->sys$poll((pollfd*)arg1, (int)arg2, (int)arg3); + return current->process().sys$poll((pollfd*)arg1, (int)arg2, (int)arg3); case Syscall::SC_munmap: - return current->sys$munmap((void*)arg1, (size_t)arg2); + return current->process().sys$munmap((void*)arg1, (size_t)arg2); case Syscall::SC_gethostname: - return current->sys$gethostname((char*)arg1, (size_t)arg2); + return current->process().sys$gethostname((char*)arg1, (size_t)arg2); case Syscall::SC_exit: cli(); - current->sys$exit((int)arg1); + current->process().sys$exit((int)arg1); ASSERT_NOT_REACHED(); return 0; case Syscall::SC_chdir: - return current->sys$chdir((const char*)arg1); + return current->process().sys$chdir((const char*)arg1); case Syscall::SC_uname: - return current->sys$uname((utsname*)arg1); + return current->process().sys$uname((utsname*)arg1); case Syscall::SC_set_mmap_name: - return current->sys$set_mmap_name((void*)arg1, (size_t)arg2, (const char*)arg3); + return current->process().sys$set_mmap_name((void*)arg1, (size_t)arg2, (const char*)arg3); case Syscall::SC_readlink: - return current->sys$readlink((const char*)arg1, (char*)arg2, (size_t)arg3); + return current->process().sys$readlink((const char*)arg1, (char*)arg2, (size_t)arg3); case Syscall::SC_ttyname_r: - return current->sys$ttyname_r((int)arg1, (char*)arg2, (size_t)arg3); + return current->process().sys$ttyname_r((int)arg1, (char*)arg2, (size_t)arg3); case Syscall::SC_ptsname_r: - return current->sys$ptsname_r((int)arg1, (char*)arg2, (size_t)arg3); + return current->process().sys$ptsname_r((int)arg1, (char*)arg2, (size_t)arg3); case Syscall::SC_setsid: - return current->sys$setsid(); + return current->process().sys$setsid(); case Syscall::SC_getsid: - return current->sys$getsid((pid_t)arg1); + return current->process().sys$getsid((pid_t)arg1); case Syscall::SC_setpgid: - return current->sys$setpgid((pid_t)arg1, (pid_t)arg2); + return current->process().sys$setpgid((pid_t)arg1, (pid_t)arg2); case Syscall::SC_getpgid: - return current->sys$getpgid((pid_t)arg1); + return current->process().sys$getpgid((pid_t)arg1); case Syscall::SC_getpgrp: - return current->sys$getpgrp(); + return current->process().sys$getpgrp(); case Syscall::SC_fork: - return current->sys$fork(regs); + return current->process().sys$fork(regs); case Syscall::SC_execve: - return current->sys$execve((const char*)arg1, (const char**)arg2, (const char**)arg3); + return current->process().sys$execve((const char*)arg1, (const char**)arg2, (const char**)arg3); case Syscall::SC_geteuid: - return current->sys$geteuid(); + return current->process().sys$geteuid(); case Syscall::SC_getegid: - return current->sys$getegid(); + return current->process().sys$getegid(); case Syscall::SC_isatty: - return current->sys$isatty((int)arg1); + return current->process().sys$isatty((int)arg1); case Syscall::SC_getdtablesize: - return current->sys$getdtablesize(); + return current->process().sys$getdtablesize(); case Syscall::SC_dup: - return current->sys$dup((int)arg1); + return current->process().sys$dup((int)arg1); case Syscall::SC_dup2: - return current->sys$dup2((int)arg1, (int)arg2); + return current->process().sys$dup2((int)arg1, (int)arg2); case Syscall::SC_sigaction: - return current->sys$sigaction((int)arg1, (const sigaction*)arg2, (sigaction*)arg3); + return current->process().sys$sigaction((int)arg1, (const sigaction*)arg2, (sigaction*)arg3); case Syscall::SC_umask: - return current->sys$umask((mode_t)arg1); + return current->process().sys$umask((mode_t)arg1); case Syscall::SC_getgroups: - return current->sys$getgroups((ssize_t)arg1, (gid_t*)arg2); + return current->process().sys$getgroups((ssize_t)arg1, (gid_t*)arg2); case Syscall::SC_setgroups: - return current->sys$setgroups((ssize_t)arg1, (const gid_t*)arg2); + return current->process().sys$setgroups((ssize_t)arg1, (const gid_t*)arg2); case Syscall::SC_sigreturn: - current->sys$sigreturn(); + current->process().sys$sigreturn(); ASSERT_NOT_REACHED(); return 0; case Syscall::SC_sigprocmask: - return current->sys$sigprocmask((int)arg1, (const sigset_t*)arg2, (sigset_t*)arg3); + return current->process().sys$sigprocmask((int)arg1, (const sigset_t*)arg2, (sigset_t*)arg3); case Syscall::SC_pipe: - return current->sys$pipe((int*)arg1); + return current->process().sys$pipe((int*)arg1); case Syscall::SC_killpg: - return current->sys$killpg((int)arg1, (int)arg2); + return current->process().sys$killpg((int)arg1, (int)arg2); case Syscall::SC_setuid: - return current->sys$setuid((uid_t)arg1); + return current->process().sys$setuid((uid_t)arg1); case Syscall::SC_setgid: - return current->sys$setgid((gid_t)arg1); + return current->process().sys$setgid((gid_t)arg1); case Syscall::SC_alarm: - return current->sys$alarm((unsigned)arg1); + return current->process().sys$alarm((unsigned)arg1); case Syscall::SC_access: - return current->sys$access((const char*)arg1, (int)arg2); + return current->process().sys$access((const char*)arg1, (int)arg2); case Syscall::SC_fcntl: - return current->sys$fcntl((int)arg1, (int)arg2, (dword)arg3); + return current->process().sys$fcntl((int)arg1, (int)arg2, (dword)arg3); case Syscall::SC_ioctl: - return current->sys$ioctl((int)arg1, (unsigned)arg2, (unsigned)arg3); + return current->process().sys$ioctl((int)arg1, (unsigned)arg2, (unsigned)arg3); case Syscall::SC_fstat: - return current->sys$fstat((int)arg1, (stat*)arg2); + return current->process().sys$fstat((int)arg1, (stat*)arg2); case Syscall::SC_mkdir: - return current->sys$mkdir((const char*)arg1, (mode_t)arg2); + return current->process().sys$mkdir((const char*)arg1, (mode_t)arg2); case Syscall::SC_times: - return current->sys$times((tms*)arg1); + return current->process().sys$times((tms*)arg1); case Syscall::SC_utime: - return current->sys$utime((const char*)arg1, (const utimbuf*)arg2); + return current->process().sys$utime((const char*)arg1, (const utimbuf*)arg2); case Syscall::SC_sync: return sync(); case Syscall::SC_link: - return current->sys$link((const char*)arg1, (const char*)arg2); + return current->process().sys$link((const char*)arg1, (const char*)arg2); case Syscall::SC_unlink: - return current->sys$unlink((const char*)arg1); + return current->process().sys$unlink((const char*)arg1); case Syscall::SC_symlink: - return current->sys$symlink((const char*)arg1, (const char*)arg2); + return current->process().sys$symlink((const char*)arg1, (const char*)arg2); case Syscall::SC_read_tsc: - return current->sys$read_tsc((dword*)arg1, (dword*)arg2); + return current->process().sys$read_tsc((dword*)arg1, (dword*)arg2); case Syscall::SC_rmdir: - return current->sys$rmdir((const char*)arg1); + return current->process().sys$rmdir((const char*)arg1); case Syscall::SC_chmod: - return current->sys$chmod((const char*)arg1, (mode_t)arg2); + return current->process().sys$chmod((const char*)arg1, (mode_t)arg2); case Syscall::SC_fchmod: - return current->sys$fchmod((int)arg1, (mode_t)arg2); + return current->process().sys$fchmod((int)arg1, (mode_t)arg2); case Syscall::SC_socket: - return current->sys$socket((int)arg1, (int)arg2, (int)arg3); + return current->process().sys$socket((int)arg1, (int)arg2, (int)arg3); case Syscall::SC_bind: - return current->sys$bind((int)arg1, (const sockaddr*)arg2, (socklen_t)arg3); + return current->process().sys$bind((int)arg1, (const sockaddr*)arg2, (socklen_t)arg3); case Syscall::SC_listen: - return current->sys$listen((int)arg1, (int)arg2); + return current->process().sys$listen((int)arg1, (int)arg2); case Syscall::SC_accept: - return current->sys$accept((int)arg1, (sockaddr*)arg2, (socklen_t*)arg3); + return current->process().sys$accept((int)arg1, (sockaddr*)arg2, (socklen_t*)arg3); case Syscall::SC_connect: - return current->sys$connect((int)arg1, (const sockaddr*)arg2, (socklen_t)arg3); + return current->process().sys$connect((int)arg1, (const sockaddr*)arg2, (socklen_t)arg3); case Syscall::SC_create_shared_buffer: - return current->sys$create_shared_buffer((pid_t)arg1, (size_t)arg2, (void**)arg3); + return current->process().sys$create_shared_buffer((pid_t)arg1, (size_t)arg2, (void**)arg3); case Syscall::SC_get_shared_buffer: - return (dword)current->sys$get_shared_buffer((int)arg1); + return (dword)current->process().sys$get_shared_buffer((int)arg1); case Syscall::SC_release_shared_buffer: - return current->sys$release_shared_buffer((int)arg1); + return current->process().sys$release_shared_buffer((int)arg1); case Syscall::SC_chown: - return current->sys$chown((const char*)arg1, (uid_t)arg2, (gid_t)arg3); + return current->process().sys$chown((const char*)arg1, (uid_t)arg2, (gid_t)arg3); case Syscall::SC_restore_signal_mask: - return current->sys$restore_signal_mask((dword)arg1); + return current->process().sys$restore_signal_mask((dword)arg1); case Syscall::SC_seal_shared_buffer: - return current->sys$seal_shared_buffer((int)arg1); + return current->process().sys$seal_shared_buffer((int)arg1); case Syscall::SC_get_shared_buffer_size: - return current->sys$get_shared_buffer_size((int)arg1); + return current->process().sys$get_shared_buffer_size((int)arg1); case Syscall::SC_sendto: - return current->sys$sendto((const SC_sendto_params*)arg1); + return current->process().sys$sendto((const SC_sendto_params*)arg1); case Syscall::SC_recvfrom: - return current->sys$recvfrom((const SC_recvfrom_params*)arg1); + return current->process().sys$recvfrom((const SC_recvfrom_params*)arg1); case Syscall::SC_getsockopt: - return current->sys$getsockopt((const SC_getsockopt_params*)arg1); + return current->process().sys$getsockopt((const SC_getsockopt_params*)arg1); case Syscall::SC_setsockopt: - return current->sys$setsockopt((const SC_setsockopt_params*)arg1); + return current->process().sys$setsockopt((const SC_setsockopt_params*)arg1); default: - kprintf("<%u> int0x82: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3); + kprintf("<%u> int0x82: Unknown function %u requested {%x, %x, %x}\n", current->process().pid(), function, arg1, arg2, arg3); break; } return 0; diff --git a/Kernel/TCPSocket.cpp b/Kernel/TCPSocket.cpp index 159c838827..2f7f759914 100644 --- a/Kernel/TCPSocket.cpp +++ b/Kernel/TCPSocket.cpp @@ -167,7 +167,7 @@ KResult TCPSocket::protocol_connect() m_state = State::Connecting; current->set_blocked_socket(this); - block(Process::BlockedConnect); + block(Thread::BlockedConnect); Scheduler::yield(); ASSERT(is_connected()); diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp new file mode 100644 index 0000000000..e3cd3d4d7a --- /dev/null +++ b/Kernel/Thread.cpp @@ -0,0 +1,511 @@ +#include <Kernel/Thread.h> +#include <Kernel/Scheduler.h> +#include <Kernel/system.h> +#include <Kernel/Process.h> +#include <Kernel/MemoryManager.h> +#include <LibC/signal_numbers.h> + +InlineLinkedList<Thread>* g_threads; +static const dword default_kernel_stack_size = 16384; +static const dword default_userspace_stack_size = 65536; + +Thread::Thread(Process& process) + : m_process(process) + , m_tid(process.m_next_tid++) +{ + dbgprintf("Thread: New thread TID=%u in %s(%u)\n", m_tid, process.name().characters(), process.pid()); + set_default_signal_dispositions(); + memset(&m_fpu_state, 0, sizeof(FPUState)); + memset(&m_tss, 0, sizeof(m_tss)); + + // Only IF is set when a process boots. + m_tss.eflags = 0x0202; + word cs, ds, ss; + + if (m_process.is_ring0()) { + cs = 0x08; + ds = 0x10; + ss = 0x10; + } else { + cs = 0x1b; + ds = 0x23; + ss = 0x23; + } + + m_tss.ds = ds; + m_tss.es = ds; + m_tss.fs = ds; + m_tss.gs = ds; + m_tss.ss = ss; + m_tss.cs = cs; + + m_tss.cr3 = m_process.page_directory().cr3(); + + if (m_process.is_ring0()) { + // FIXME: This memory is leaked. + // But uh, there's also no kernel process termination, so I guess it's not technically leaked... + dword stack_bottom = (dword)kmalloc_eternal(default_kernel_stack_size); + m_stack_top0 = (stack_bottom + default_kernel_stack_size) & 0xffffff8; + m_tss.esp = m_stack_top0; + } else { + // Ring3 processes need a separate stack for Ring0. + m_kernel_stack = kmalloc(default_kernel_stack_size); + m_stack_top0 = ((dword)m_kernel_stack + default_kernel_stack_size) & 0xffffff8; + m_tss.ss0 = 0x10; + m_tss.esp0 = m_stack_top0; + } + + // HACK: Ring2 SS in the TSS is the current PID. + m_tss.ss2 = m_process.pid(); + m_far_ptr.offset = 0x98765432; + + InterruptDisabler disabler; + g_threads->prepend(this); +} + +Thread::~Thread() +{ + dbgprintf("~Thread{%p}\n", this); + { + InterruptDisabler disabler; + g_threads->remove(this); + } + + if (g_last_fpu_thread == this) + g_last_fpu_thread = nullptr; + + if (selector()) + gdt_free_entry(selector()); + + if (m_kernel_stack) { + kfree(m_kernel_stack); + m_kernel_stack = nullptr; + } + + if (m_kernel_stack_for_signal_handler) { + kfree(m_kernel_stack_for_signal_handler); + m_kernel_stack_for_signal_handler = nullptr; + } +} + +void Thread::unblock() +{ + if (current == this) { + system.nblocked--; + m_state = Thread::Running; + return; + } + ASSERT(m_state != Thread::Runnable && m_state != Thread::Running); + system.nblocked--; + m_state = Thread::Runnable; +} + +void Thread::snooze_until(Alarm& alarm) +{ + m_snoozing_alarm = &alarm; + block(Thread::BlockedSnoozing); + Scheduler::yield(); +} + +void Thread::block(Thread::State new_state) +{ + if (state() != Thread::Running) { + kprintf("Thread::block: %s(%u) block(%u/%s) with state=%u/%s\n", process().name().characters(), process().pid(), new_state, to_string(new_state), state(), to_string(state())); + } + ASSERT(state() == Thread::Running); + system.nblocked++; + m_was_interrupted_while_blocked = false; + set_state(new_state); +} + +void block(Thread::State state) +{ + current->block(state); + Scheduler::yield(); +} + +void sleep(dword ticks) +{ + ASSERT(current->state() == Thread::Running); + current->set_wakeup_time(system.uptime + ticks); + current->block(Thread::BlockedSleep); + Scheduler::yield(); +} + +const char* to_string(Thread::State state) +{ + switch (state) { + case Thread::Invalid: return "Invalid"; + case Thread::Runnable: return "Runnable"; + case Thread::Running: return "Running"; + case Thread::Dying: return "Dying"; + case Thread::Dead: return "Dead"; + case Thread::Stopped: return "Stopped"; + case Thread::Skip1SchedulerPass: return "Skip1"; + case Thread::Skip0SchedulerPasses: return "Skip0"; + case Thread::BlockedSleep: return "Sleep"; + case Thread::BlockedWait: return "Wait"; + case Thread::BlockedRead: return "Read"; + case Thread::BlockedWrite: return "Write"; + case Thread::BlockedSignal: return "Signal"; + case Thread::BlockedSelect: return "Select"; + case Thread::BlockedLurking: return "Lurking"; + case Thread::BlockedConnect: return "Connect"; + case Thread::BlockedReceive: return "Receive"; + case Thread::BlockedSnoozing: return "Snoozing"; + } + kprintf("to_string(Thread::State): Invalid state: %u\n", state); + ASSERT_NOT_REACHED(); + return nullptr; +} + +void Thread::finalize() +{ + dbgprintf("Finalizing Thread %u in %s(%u)\n", tid(), m_process.name().characters(), pid()); + m_blocked_socket = nullptr; + set_state(Thread::State::Dead); + + if (this == &m_process.main_thread()) + m_process.finalize(); +} + +void Thread::finalize_dying_threads() +{ + Vector<Thread*> dying_threads; + { + InterruptDisabler disabler; + for_each_in_state(Thread::State::Dying, [&] (Thread& thread) { + dying_threads.append(&thread); + }); + } + for (auto* thread : dying_threads) + thread->finalize(); +} + +bool Thread::tick() +{ + ++m_ticks; + if (tss().cs & 3) + ++m_process.m_ticks_in_user; + else + ++m_process.m_ticks_in_kernel; + return --m_ticks_left; +} + +void Thread::send_signal(byte signal, Process* sender) +{ + ASSERT(signal < 32); + + if (sender) + dbgprintf("signal: %s(%u) sent %d to %s(%u)\n", sender->name().characters(), sender->pid(), signal, process().name().characters(), pid()); + else + dbgprintf("signal: kernel sent %d to %s(%u)\n", signal, process().name().characters(), pid()); + + InterruptDisabler disabler; + m_pending_signals |= 1 << signal; +} + +bool Thread::has_unmasked_pending_signals() const +{ + return m_pending_signals & ~m_signal_mask; +} + +ShouldUnblockThread Thread::dispatch_one_pending_signal() +{ + ASSERT_INTERRUPTS_DISABLED(); + dword signal_candidates = m_pending_signals & ~m_signal_mask; + ASSERT(signal_candidates); + + byte signal = 0; + for (; signal < 32; ++signal) { + if (signal_candidates & (1 << signal)) { + break; + } + } + return dispatch_signal(signal); +} + +enum class DefaultSignalAction { + Terminate, + Ignore, + DumpCore, + Stop, + Continue, +}; + +DefaultSignalAction default_signal_action(byte signal) +{ + ASSERT(signal && signal < NSIG); + + switch (signal) { + case SIGHUP: + case SIGINT: + case SIGKILL: + case SIGPIPE: + case SIGALRM: + case SIGUSR1: + case SIGUSR2: + case SIGVTALRM: + case SIGSTKFLT: + case SIGIO: + case SIGPROF: + case SIGTERM: + case SIGPWR: + return DefaultSignalAction::Terminate; + case SIGCHLD: + case SIGURG: + case SIGWINCH: + return DefaultSignalAction::Ignore; + case SIGQUIT: + case SIGILL: + case SIGTRAP: + case SIGABRT: + case SIGBUS: + case SIGFPE: + case SIGSEGV: + case SIGXCPU: + case SIGXFSZ: + case SIGSYS: + return DefaultSignalAction::DumpCore; + case SIGCONT: + return DefaultSignalAction::Continue; + case SIGSTOP: + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + return DefaultSignalAction::Stop; + } + ASSERT_NOT_REACHED(); +} + +ShouldUnblockThread Thread::dispatch_signal(byte signal) +{ + ASSERT_INTERRUPTS_DISABLED(); + ASSERT(signal < 32); + +#ifdef SIGNAL_DEBUG + kprintf("dispatch_signal %s(%u) <- %u\n", name().characters(), pid(), signal); +#endif + + auto& action = m_signal_action_data[signal]; + // FIXME: Implement SA_SIGINFO signal handlers. + ASSERT(!(action.flags & SA_SIGINFO)); + + // Mark this signal as handled. + m_pending_signals &= ~(1 << signal); + + if (signal == SIGSTOP) { + set_state(Stopped); + return ShouldUnblockThread::No; + } + + if (signal == SIGCONT && state() == Stopped) + set_state(Runnable); + + auto handler_laddr = action.handler_or_sigaction; + if (handler_laddr.is_null()) { + switch (default_signal_action(signal)) { + case DefaultSignalAction::Stop: + set_state(Stopped); + return ShouldUnblockThread::No; + case DefaultSignalAction::DumpCore: + case DefaultSignalAction::Terminate: + m_process.terminate_due_to_signal(signal); + return ShouldUnblockThread::No; + case DefaultSignalAction::Ignore: + return ShouldUnblockThread::No; + case DefaultSignalAction::Continue: + return ShouldUnblockThread::Yes; + } + ASSERT_NOT_REACHED(); + } + + if (handler_laddr.as_ptr() == SIG_IGN) { +#ifdef SIGNAL_DEBUG + kprintf("%s(%u) ignored signal %u\n", name().characters(), pid(), signal); +#endif + return ShouldUnblockThread::Yes; + } + + dword old_signal_mask = m_signal_mask; + dword new_signal_mask = action.mask; + if (action.flags & SA_NODEFER) + new_signal_mask &= ~(1 << signal); + else + new_signal_mask |= 1 << signal; + + m_signal_mask |= new_signal_mask; + + Scheduler::prepare_to_modify_tss(*this); + + word ret_cs = m_tss.cs; + dword ret_eip = m_tss.eip; + dword ret_eflags = m_tss.eflags; + bool interrupting_in_kernel = (ret_cs & 3) == 0; + + ProcessPagingScope paging_scope(m_process); + m_process.create_signal_trampolines_if_needed(); + + if (interrupting_in_kernel) { +#ifdef SIGNAL_DEBUG + kprintf("dispatch_signal to %s(%u) in state=%s with return to %w:%x\n", name().characters(), pid(), to_string(state()), ret_cs, ret_eip); +#endif + ASSERT(is_blocked()); + m_tss_to_resume_kernel = m_tss; +#ifdef SIGNAL_DEBUG + kprintf("resume tss pc: %w:%x stack: %w:%x flags: %x cr3: %x\n", m_tss_to_resume_kernel.cs, m_tss_to_resume_kernel.eip, m_tss_to_resume_kernel.ss, m_tss_to_resume_kernel.esp, m_tss_to_resume_kernel.eflags, m_tss_to_resume_kernel.cr3); +#endif + + if (!m_signal_stack_user_region) { + m_signal_stack_user_region = m_process.allocate_region(LinearAddress(), default_userspace_stack_size, "Signal stack (user)"); + ASSERT(m_signal_stack_user_region); + } + if (!m_kernel_stack_for_signal_handler) { + m_kernel_stack_for_signal_handler = kmalloc(default_kernel_stack_size); + ASSERT(m_kernel_stack_for_signal_handler); + } + m_tss.ss = 0x23; + m_tss.esp = m_signal_stack_user_region->laddr().offset(default_userspace_stack_size).get(); + m_tss.ss0 = 0x10; + m_tss.esp0 = (dword)m_kernel_stack_for_signal_handler + default_kernel_stack_size; + + push_value_on_stack(0); + } else { + push_value_on_stack(ret_eip); + push_value_on_stack(ret_eflags); + + // PUSHA + dword old_esp = m_tss.esp; + push_value_on_stack(m_tss.eax); + push_value_on_stack(m_tss.ecx); + push_value_on_stack(m_tss.edx); + push_value_on_stack(m_tss.ebx); + push_value_on_stack(old_esp); + push_value_on_stack(m_tss.ebp); + push_value_on_stack(m_tss.esi); + push_value_on_stack(m_tss.edi); + + // Align the stack. + m_tss.esp -= 12; + } + + // PUSH old_signal_mask + push_value_on_stack(old_signal_mask); + + m_tss.cs = 0x1b; + m_tss.ds = 0x23; + m_tss.es = 0x23; + m_tss.fs = 0x23; + m_tss.gs = 0x23; + m_tss.eip = handler_laddr.get(); + + // FIXME: Should we worry about the stack being 16 byte aligned when entering a signal handler? + push_value_on_stack(signal); + + if (interrupting_in_kernel) + push_value_on_stack(m_process.m_return_to_ring0_from_signal_trampoline.get()); + else + push_value_on_stack(m_process.m_return_to_ring3_from_signal_trampoline.get()); + + ASSERT((m_tss.esp % 16) == 0); + + // FIXME: This state is such a hack. It avoids trouble if 'current' is the process receiving a signal. + set_state(Skip1SchedulerPass); + +#ifdef SIGNAL_DEBUG + kprintf("signal: Okay, %s(%u) {%s} has been primed with signal handler %w:%x\n", name().characters(), pid(), to_string(state()), m_tss.cs, m_tss.eip); +#endif + return ShouldUnblockThread::Yes; +} + +void Thread::set_default_signal_dispositions() +{ + // FIXME: Set up all the right default actions. See signal(7). + memset(&m_signal_action_data, 0, sizeof(m_signal_action_data)); + m_signal_action_data[SIGCHLD].handler_or_sigaction = LinearAddress((dword)SIG_IGN); + m_signal_action_data[SIGWINCH].handler_or_sigaction = LinearAddress((dword)SIG_IGN); +} + +void Thread::push_value_on_stack(dword value) +{ + m_tss.esp -= 4; + dword* stack_ptr = (dword*)m_tss.esp; + *stack_ptr = value; +} + +void Thread::make_userspace_stack(Vector<String> arguments, Vector<String> environment) +{ + auto* region = m_process.allocate_region(LinearAddress(), default_userspace_stack_size, "stack"); + ASSERT(region); + m_stack_top3 = region->laddr().offset(default_userspace_stack_size).get(); + m_tss.esp = m_stack_top3; + + char* stack_base = (char*)region->laddr().get(); + int argc = arguments.size(); + char** argv = (char**)stack_base; + char** env = argv + arguments.size() + 1; + char* bufptr = stack_base + (sizeof(char*) * (arguments.size() + 1)) + (sizeof(char*) * (environment.size() + 1)); + + size_t total_blob_size = 0; + for (auto& a : arguments) + total_blob_size += a.length() + 1; + for (auto& e : environment) + total_blob_size += e.length() + 1; + + size_t total_meta_size = sizeof(char*) * (arguments.size() + 1) + sizeof(char*) * (environment.size() + 1); + + // FIXME: It would be better if this didn't make us panic. + ASSERT((total_blob_size + total_meta_size) < default_userspace_stack_size); + + for (int i = 0; i < arguments.size(); ++i) { + argv[i] = bufptr; + memcpy(bufptr, arguments[i].characters(), arguments[i].length()); + bufptr += arguments[i].length(); + *(bufptr++) = '\0'; + } + argv[arguments.size()] = nullptr; + + for (int i = 0; i < environment.size(); ++i) { + env[i] = bufptr; + memcpy(bufptr, environment[i].characters(), environment[i].length()); + bufptr += environment[i].length(); + *(bufptr++) = '\0'; + } + env[environment.size()] = nullptr; + + // NOTE: The stack needs to be 16-byte aligned. + push_value_on_stack((dword)env); + push_value_on_stack((dword)argv); + push_value_on_stack((dword)argc); + push_value_on_stack(0); +} + +Thread* Thread::clone(Process& process) +{ + auto* clone = new Thread(process); + memcpy(clone->m_signal_action_data, m_signal_action_data, sizeof(m_signal_action_data)); + clone->m_signal_mask = m_signal_mask; + //clone->m_tss = m_tss; + clone->m_fpu_state = m_fpu_state; + clone->m_has_used_fpu = m_has_used_fpu; + return clone; +} + +KResult Thread::wait_for_connect(Socket& socket) +{ + if (socket.is_connected()) + return KSuccess; + m_blocked_socket = socket; + block(Thread::State::BlockedConnect); + Scheduler::yield(); + m_blocked_socket = nullptr; + if (!socket.is_connected()) + return KResult(-ECONNREFUSED); + return KSuccess; +} + +void Thread::initialize() +{ + g_threads = new InlineLinkedList<Thread>; + Scheduler::initialize(); +} diff --git a/Kernel/Thread.h b/Kernel/Thread.h new file mode 100644 index 0000000000..85be55caa7 --- /dev/null +++ b/Kernel/Thread.h @@ -0,0 +1,205 @@ +#pragma once + +#include <Kernel/i386.h> +#include <Kernel/TSS.h> +#include <Kernel/KResult.h> +#include <AK/AKString.h> +#include <AK/InlineLinkedList.h> +#include <AK/RetainPtr.h> +#include <AK/Vector.h> + +class Alarm; +class Process; +class Region; +class Socket; + +enum class ShouldUnblockThread { No = 0, Yes }; + +struct SignalActionData { + LinearAddress handler_or_sigaction; + dword mask { 0 }; + int flags { 0 }; + LinearAddress restorer; +}; + +class Thread : public InlineLinkedListNode<Thread> { + friend class Process; + friend class Scheduler; +public: + explicit Thread(Process&); + ~Thread(); + + static void initialize(); + static void finalize_dying_threads(); + + int tid() const { return m_tid; } + int pid() const; + + Process& process() { return m_process; } + const Process& process() const { return m_process; } + + void finalize(); + + enum State { + Invalid = 0, + Runnable, + Running, + Skip1SchedulerPass, + Skip0SchedulerPasses, + Dying, + Dead, + Stopped, + BlockedLurking, + BlockedSleep, + BlockedWait, + BlockedRead, + BlockedWrite, + BlockedSignal, + BlockedSelect, + BlockedConnect, + BlockedReceive, + BlockedSnoozing, + }; + + void did_schedule() { ++m_times_scheduled; } + dword times_scheduled() const { return m_times_scheduled; } + + bool is_stopped() const { return m_state == Stopped; } + bool is_blocked() const + { + return m_state == BlockedSleep || m_state == BlockedWait || m_state == BlockedRead || m_state == BlockedWrite || m_state == BlockedSignal || m_state == BlockedSelect; + } + bool in_kernel() const { return (m_tss.cs & 0x03) == 0; } + + dword frame_ptr() const { return m_tss.ebp; } + dword stack_ptr() const { return m_tss.esp; } + dword stack_top() const { return m_tss.ss == 0x10 ? m_stack_top0 : m_stack_top3; } + + word selector() const { return m_far_ptr.selector; } + TSS32& tss() { return m_tss; } + State state() const { return m_state; } + dword ticks() const { return m_ticks; } + pid_t waitee_pid() const { return m_waitee_pid; } + + void block(Thread::State); + void unblock(); + + void set_wakeup_time(dword t) { m_wakeup_time = t; } + dword wakeup_time() const { return m_wakeup_time; } + void snooze_until(Alarm&); + KResult wait_for_connect(Socket&); + + const FarPtr& far_ptr() const { return m_far_ptr; } + + bool tick(); + void set_ticks_left(dword t) { m_ticks_left = t; } + dword ticks_left() const { return m_ticks_left; } + + dword kernel_stack_base() const { return (dword)m_kernel_stack; } + dword kernel_stack_for_signal_handler_base() const { return (dword)m_kernel_stack_for_signal_handler; } + + void set_selector(word s) { m_far_ptr.selector = s; } + void set_state(State s) { m_state = s; } + + void send_signal(byte signal, Process* sender); + + ShouldUnblockThread dispatch_one_pending_signal(); + ShouldUnblockThread dispatch_signal(byte signal); + bool has_unmasked_pending_signals() const; + void terminate_due_to_signal(byte signal); + + FPUState& fpu_state() { return m_fpu_state; } + bool has_used_fpu() const { return m_has_used_fpu; } + void set_has_used_fpu(bool b) { m_has_used_fpu = b; } + + void set_blocked_socket(Socket* socket) { m_blocked_socket = socket; } + + void set_default_signal_dispositions(); + void push_value_on_stack(dword); + void make_userspace_stack(Vector<String> arguments, Vector<String> environment); + + Thread* clone(Process&); + + // For InlineLinkedList + Thread* m_prev { nullptr }; + Thread* m_next { nullptr }; + + template<typename Callback> static void for_each_in_state(State, Callback); + template<typename Callback> static void for_each_living(Callback); + template<typename Callback> static void for_each(Callback); + +private: + Process& m_process; + int m_tid { -1 }; + TSS32 m_tss; + TSS32 m_tss_to_resume_kernel; + FarPtr m_far_ptr; + dword m_ticks { 0 }; + dword m_ticks_left { 0 }; + dword m_stack_top0 { 0 }; + dword m_stack_top3 { 0 }; + dword m_wakeup_time { 0 }; + dword m_times_scheduled { 0 }; + dword m_pending_signals { 0 }; + dword m_signal_mask { 0 }; + void* m_kernel_stack { nullptr }; + void* m_kernel_stack_for_signal_handler { nullptr }; + pid_t m_waitee_pid { -1 }; + int m_blocked_fd { -1 }; + timeval m_select_timeout; + SignalActionData m_signal_action_data[32]; + RetainPtr<Socket> m_blocked_socket; + Region* m_signal_stack_user_region { nullptr }; + Alarm* m_snoozing_alarm { nullptr }; + Vector<int> m_select_read_fds; + Vector<int> m_select_write_fds; + Vector<int> m_select_exceptional_fds; + State m_state { Invalid }; + FPUState m_fpu_state; + bool m_select_has_timeout { false }; + bool m_has_used_fpu { false }; + bool m_was_interrupted_while_blocked { false }; +}; + +extern InlineLinkedList<Thread>* g_threads; + +const char* to_string(Thread::State); +void block(Thread::State); +void sleep(dword ticks); + +template<typename Callback> +inline void Thread::for_each_in_state(State state, Callback callback) +{ + ASSERT_INTERRUPTS_DISABLED(); + for (auto* thread = g_threads->head(); thread;) { + auto* next_thread = thread->next(); + if (thread->state() == state) + callback(*thread); + thread = next_thread; + } +} + +template<typename Callback> +inline void Thread::for_each_living(Callback callback) +{ + ASSERT_INTERRUPTS_DISABLED(); + for (auto* thread = g_threads->head(); thread;) { + auto* next_thread = thread->next(); + if (thread->state() != Thread::State::Dead && thread->state() != Thread::State::Dying) + callback(*thread); + thread = next_thread; + } +} + +template<typename Callback> +inline void Thread::for_each(Callback callback) +{ + ASSERT_INTERRUPTS_DISABLED(); + for (auto* thread = g_threads->head(); thread;) { + auto* next_thread = thread->next(); + if (callback(*thread) == IterationDecision::Abort) + return; + thread = next_thread; + } +} + diff --git a/Kernel/VirtualFileSystem.cpp b/Kernel/VirtualFileSystem.cpp index 1f65d6742a..e03efde33c 100644 --- a/Kernel/VirtualFileSystem.cpp +++ b/Kernel/VirtualFileSystem.cpp @@ -138,7 +138,7 @@ KResult VFS::utime(const String& path, Inode& base, time_t atime, time_t mtime) auto& inode = *descriptor_or_error.value()->inode(); if (inode.fs().is_readonly()) return KResult(-EROFS); - if (inode.metadata().uid != current->euid()) + if (inode.metadata().uid != current->process().euid()) return KResult(-EACCES); int error = inode.set_atime(atime); @@ -175,11 +175,11 @@ KResultOr<Retained<FileDescriptor>> VFS::open(const String& path, int options, m // NOTE: Read permission is a bit weird, since O_RDONLY == 0, // so we check if (NOT write_only OR read_and_write) if (!(options & O_WRONLY) || (options & O_RDWR)) { - if (!metadata.may_read(*current)) + if (!metadata.may_read(current->process())) return KResult(-EACCES); } if ((options & O_WRONLY) || (options & O_RDWR)) { - if (!metadata.may_write(*current)) + if (!metadata.may_write(current->process())) return KResult(-EACCES); if (metadata.is_directory()) return KResult(-EISDIR); @@ -188,7 +188,6 @@ KResultOr<Retained<FileDescriptor>> VFS::open(const String& path, int options, m if (metadata.is_device()) { auto it = m_devices.find(encoded_device(metadata.major_device, metadata.minor_device)); if (it == m_devices.end()) { - kprintf("VFS::open: no such device %u,%u\n", metadata.major_device, metadata.minor_device); return KResult(-ENODEV); } auto descriptor_or_error = (*it).value->open(options); @@ -217,7 +216,7 @@ KResultOr<Retained<FileDescriptor>> VFS::create(const String& path, int options, return KResult(-ENOENT); if (existing_file_or_error.error() != -ENOENT) return existing_file_or_error.error(); - if (!parent_inode->metadata().may_write(*current)) + if (!parent_inode->metadata().may_write(current->process())) return KResult(-EACCES); FileSystemPath p(path); @@ -241,7 +240,7 @@ KResult VFS::mkdir(const String& path, mode_t mode, Inode& base) if (result.error() != -ENOENT) return result.error(); - if (!parent_inode->metadata().may_write(*current)) + if (!parent_inode->metadata().may_write(current->process())) return KResult(-EACCES); FileSystemPath p(path); @@ -261,15 +260,15 @@ KResult VFS::access(const String& path, int mode, Inode& base) auto inode = inode_or_error.value(); auto metadata = inode->metadata(); if (mode & R_OK) { - if (!metadata.may_read(*current)) + if (!metadata.may_read(current->process())) return KResult(-EACCES); } if (mode & W_OK) { - if (!metadata.may_write(*current)) + if (!metadata.may_write(current->process())) return KResult(-EACCES); } if (mode & X_OK) { - if (!metadata.may_execute(*current)) + if (!metadata.may_execute(current->process())) return KResult(-EACCES); } return KSuccess; @@ -283,7 +282,7 @@ KResultOr<Retained<Inode>> VFS::open_directory(const String& path, Inode& base) auto inode = inode_or_error.value(); if (!inode->is_directory()) return KResult(-ENOTDIR); - if (!inode->metadata().may_execute(*current)) + if (!inode->metadata().may_execute(current->process())) return KResult(-EACCES); return Retained<Inode>(*inode); } @@ -293,7 +292,7 @@ KResult VFS::chmod(Inode& inode, mode_t mode) if (inode.fs().is_readonly()) return KResult(-EROFS); - if (current->euid() != inode.metadata().uid && !current->is_superuser()) + if (current->process().euid() != inode.metadata().uid && !current->process().is_superuser()) return KResult(-EPERM); // Only change the permission bits. @@ -320,19 +319,19 @@ KResult VFS::chown(const String& path, uid_t a_uid, gid_t a_gid, Inode& base) if (inode->fs().is_readonly()) return KResult(-EROFS); - if (current->euid() != inode->metadata().uid && !current->is_superuser()) + if (current->process().euid() != inode->metadata().uid && !current->process().is_superuser()) return KResult(-EPERM); uid_t new_uid = inode->metadata().uid; gid_t new_gid = inode->metadata().gid; if (a_uid != (uid_t)-1) { - if (current->euid() != a_uid && !current->is_superuser()) + if (current->process().euid() != a_uid && !current->process().is_superuser()) return KResult(-EPERM); new_uid = a_uid; } if (a_gid != (gid_t)-1) { - if (!current->in_group(a_gid) && !current->is_superuser()) + if (!current->process().in_group(a_gid) && !current->process().is_superuser()) return KResult(-EPERM); new_gid = a_gid; } @@ -377,7 +376,7 @@ KResult VFS::link(const String& old_path, const String& new_path, Inode& base) if (parent_inode->fs().is_readonly()) return KResult(-EROFS); - if (!parent_inode->metadata().may_write(*current)) + if (!parent_inode->metadata().may_write(current->process())) return KResult(-EACCES); return parent_inode->add_child(old_inode->identifier(), FileSystemPath(new_path).basename(), 0); @@ -394,7 +393,7 @@ KResult VFS::unlink(const String& path, Inode& base) if (inode->is_directory()) return KResult(-EISDIR); - if (!parent_inode->metadata().may_write(*current)) + if (!parent_inode->metadata().may_write(current->process())) return KResult(-EACCES); return parent_inode->remove_child(FileSystemPath(path).basename()); @@ -410,7 +409,7 @@ KResult VFS::symlink(const String& target, const String& linkpath, Inode& base) return KResult(-ENOENT); if (existing_file_or_error.error() != -ENOENT) return existing_file_or_error.error(); - if (!parent_inode->metadata().may_write(*current)) + if (!parent_inode->metadata().may_write(current->process())) return KResult(-EACCES); FileSystemPath p(linkpath); @@ -442,7 +441,7 @@ KResult VFS::rmdir(const String& path, Inode& base) if (!inode->is_directory()) return KResult(-ENOTDIR); - if (!parent_inode->metadata().may_write(*current)) + if (!parent_inode->metadata().may_write(current->process())) return KResult(-EACCES); if (inode->directory_entry_count() != 2) @@ -561,7 +560,7 @@ KResultOr<InodeIdentifier> VFS::resolve_path(const String& path, InodeIdentifier #endif return KResult(-ENOTDIR); } - if (!metadata.may_execute(*current)) + if (!metadata.may_execute(current->process())) return KResult(-EACCES); auto parent = crumb_id; crumb_id = crumb_inode->lookup(part); @@ -612,7 +611,6 @@ KResultOr<InodeIdentifier> VFS::resolve_path(const String& path, InodeIdentifier ASSERT(crumb_id.is_valid()); } } - return crumb_id; } diff --git a/Kernel/i386.cpp b/Kernel/i386.cpp index 4feb155e26..44103f0b82 100644 --- a/Kernel/i386.cpp +++ b/Kernel/i386.cpp @@ -126,7 +126,7 @@ static void dump(const DumpType& regs) { word ss; dword esp; - if (current->is_ring0()) { + if (!current || current->process().is_ring0()) { ss = regs.ds; esp = regs.esp; } else { @@ -144,7 +144,7 @@ static void dump(const DumpType& regs) kprintf("eax=%x ebx=%x ecx=%x edx=%x\n", regs.eax, regs.ebx, regs.ecx, regs.edx); kprintf("ebp=%x esp=%x esi=%x edi=%x\n", regs.ebp, esp, regs.esi, regs.edi); - if (current->validate_read((void*)regs.eip, 8)) { + if (current && current->process().validate_read((void*)regs.eip, 8)) { byte* codeptr = (byte*)regs.eip; kprintf("code: %b %b %b %b %b %b %b %b\n", codeptr[0], @@ -163,17 +163,21 @@ static void dump(const DumpType& regs) EH_ENTRY_NO_CODE(6); void exception_6_handler(RegisterDump& regs) { - kprintf("%s invalid opcode: %u(%s)\n", current->is_ring0() ? "Kernel" : "Process", current->pid(), current->name().characters()); + if (!current) { + kprintf("#UD with !current\n"); + hang(); + } + kprintf("%s invalid opcode: %u(%s)\n", current->process().is_ring0() ? "Kernel" : "Process", current->pid(), current->process().name().characters()); dump(regs); - if (current->is_ring0()) { + if (current->process().is_ring0()) { kprintf("Oh shit, we've crashed in ring 0 :(\n"); hang(); } hang(); - current->crash(); + current->process().crash(); } // 7: FPU not available exception @@ -183,14 +187,14 @@ void exception_7_handler(RegisterDump& regs) (void)regs; asm volatile("clts"); - if (g_last_fpu_process == current) + if (g_last_fpu_thread == current) return; - if (g_last_fpu_process) { - asm volatile("fnsave %0":"=m"(g_last_fpu_process->fpu_state())); + if (g_last_fpu_thread) { + asm volatile("fnsave %0":"=m"(g_last_fpu_thread->fpu_state())); } else { asm volatile("fnclex"); } - g_last_fpu_process = current; + g_last_fpu_thread = current; if (current->has_used_fpu()) { asm volatile("frstor %0"::"m"(current->fpu_state())); @@ -200,7 +204,7 @@ void exception_7_handler(RegisterDump& regs) } #ifdef FPU_EXCEPTION_DEBUG - kprintf("%s FPU not available exception: %u(%s)\n", current->is_ring0() ? "Kernel" : "Process", current->pid(), current->name().characters()); + kprintf("%s FPU not available exception: %u(%s)\n", current->process().is_ring0() ? "Kernel" : "Process", current->pid(), current->process().name().characters()); dump(regs); #endif } @@ -210,16 +214,16 @@ void exception_7_handler(RegisterDump& regs) EH_ENTRY_NO_CODE(0); void exception_0_handler(RegisterDump& regs) { - kprintf("%s DIVIDE ERROR: %u(%s)\n", current->is_ring0() ? "Kernel" : "User", current->pid(), current->name().characters()); + kprintf("%s DIVIDE ERROR: %u(%s)\n", current->process().is_ring0() ? "Kernel" : "User", current->pid(), current->process().name().characters()); dump(regs); - if (current->is_ring0()) { + if (current->process().is_ring0()) { kprintf("Oh shit, we've crashed in ring 0 :(\n"); hang(); } - current->crash(); + current->process().crash(); } @@ -227,16 +231,16 @@ void exception_0_handler(RegisterDump& regs) EH_ENTRY(13); void exception_13_handler(RegisterDumpWithExceptionCode& regs) { - kprintf("%s GPF: %u(%s)\n", current->is_ring0() ? "Kernel" : "User", current->pid(), current->name().characters()); + kprintf("%s GPF: %u(%s)\n", current->process().is_ring0() ? "Kernel" : "User", current->pid(), current->process().name().characters()); dump(regs); - if (current->is_ring0()) { + if (current->process().is_ring0()) { kprintf("Oh shit, we've crashed in ring 0 :(\n"); hang(); } - current->crash(); + current->process().crash(); } // 14: Page Fault @@ -253,7 +257,7 @@ void exception_14_handler(RegisterDumpWithExceptionCode& regs) #ifdef PAGE_FAULT_DEBUG dbgprintf("%s(%u): ring%u %s page fault in PD=%x, %s L%x\n", - current->name().characters(), + current->process().name().characters(), current->pid(), regs.cs & 3, regs.exception_code & 1 ? "PV" : "NP", @@ -270,12 +274,13 @@ void exception_14_handler(RegisterDumpWithExceptionCode& regs) if (response == PageFaultResponse::ShouldCrash) { kprintf("%s(%u) unrecoverable page fault, %s laddr=%p\n", - current->name().characters(), + current->process().name().characters(), current->pid(), regs.exception_code & 2 ? "write" : "read", faultAddress); dump(regs); - current->crash(); + hang(); + current->process().crash(); } else if (response == PageFaultResponse::Continue) { #ifdef PAGE_FAULT_DEBUG dbgprintf("Continuing after resolved page fault\n"); diff --git a/Kernel/init.cpp b/Kernel/init.cpp index b397255cc8..6fc8797bf3 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -26,9 +26,9 @@ #include "E1000NetworkAdapter.h" #include <Kernel/NetworkTask.h> -#define SPAWN_LAUNCHER +//#define SPAWN_LAUNCHER //#define SPAWN_GUITEST2 -//#define SPAWN_FILE_MANAGER +#define SPAWN_FILE_MANAGER //#define SPAWN_PROCESS_MANAGER //#define SPAWN_TEXT_EDITOR //#define SPAWN_FONTEDITOR @@ -78,7 +78,9 @@ VFS* vfs; vfs->mount_root(e2fs.copy_ref()); + dbgprintf("Load ksyms\n"); load_ksyms(); + dbgprintf("Loaded ksyms\n"); vfs->mount(ProcFS::the(), "/proc"); vfs->mount(DevPtsFS::the(), "/dev/pts"); @@ -130,7 +132,7 @@ VFS* vfs; Process::create_kernel_process("spawn_stress", spawn_stress); #endif - current->sys$exit(0); + current->process().sys$exit(0); ASSERT_NOT_REACHED(); } @@ -177,6 +179,7 @@ VFS* vfs; devptsfs->initialize(); Process::initialize(); + Thread::initialize(); Process::create_kernel_process("init_stage2", init_stage2); Process::create_kernel_process("syncd", [] { for (;;) { @@ -186,10 +189,10 @@ VFS* vfs; }); Process::create_kernel_process("Finalizer", [] { g_finalizer = current; - current->set_priority(Process::LowPriority); + current->process().set_priority(Process::LowPriority); for (;;) { - Process::finalize_dying_processes(); - current->block(Process::BlockedLurking); + Thread::finalize_dying_threads(); + current->block(Thread::BlockedLurking); Scheduler::yield(); } }); diff --git a/Kernel/kassert.h b/Kernel/kassert.h index 774afe2a62..e40b26deb6 100644 --- a/Kernel/kassert.h +++ b/Kernel/kassert.h @@ -8,6 +8,7 @@ #define ASSERT(expr) (static_cast<bool>(expr) ? (void)0 : __assertion_failed(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__)) #define CRASH() do { asm volatile("ud2"); } while(0) #define RELEASE_ASSERT(x) do { if (!(x)) CRASH(); } while(0) +//#define ASSERT RELEASE_ASSERT #define ASSERT_NOT_REACHED() ASSERT(false) #define ASSERT_INTERRUPTS_DISABLED() ASSERT(!(cpu_flags() & 0x200)) #define ASSERT_INTERRUPTS_ENABLED() ASSERT(cpu_flags() & 0x200) diff --git a/Kernel/kmalloc.cpp b/Kernel/kmalloc.cpp index 4689a9b196..7e448ce2f2 100644 --- a/Kernel/kmalloc.cpp +++ b/Kernel/kmalloc.cpp @@ -101,7 +101,7 @@ void* kmalloc_impl(size_t size) real_size = size + sizeof(allocation_t); if (sum_free < real_size) { - kprintf("%s<%u> kmalloc(): PANIC! Out of memory (sucks, dude)\nsum_free=%u, real_size=%u\n", current->name().characters(), current->pid(), sum_free, real_size); + kprintf("%s<%u> kmalloc(): PANIC! Out of memory (sucks, dude)\nsum_free=%u, real_size=%u\n", current->process().name().characters(), current->pid(), sum_free, real_size); hang(); } @@ -161,7 +161,7 @@ void* kmalloc_impl(size_t size) } } - kprintf("%s<%u> kmalloc(): PANIC! Out of memory (no suitable block for size %u)\n", current->name().characters(), current->pid(), size); + kprintf("%s<%u> kmalloc(): PANIC! Out of memory (no suitable block for size %u)\n", current->process().name().characters(), current->pid(), size); hang(); } diff --git a/Kernel/kprintf.cpp b/Kernel/kprintf.cpp index b4c52b6905..cdcc67c7b4 100644 --- a/Kernel/kprintf.cpp +++ b/Kernel/kprintf.cpp @@ -8,7 +8,11 @@ static void console_putch(char*&, char ch) { - Console::the().write(*current, (byte*)&ch, 1); + if (!current) { + IO::out8(0xe9, ch); + return; + } + Console::the().write(current->process(), (byte*)&ch, 1); } int kprintf(const char* fmt, ...) diff --git a/SharedGraphics/PNGLoader.cpp b/SharedGraphics/PNGLoader.cpp index f248571cfb..97d2c62983 100644 --- a/SharedGraphics/PNGLoader.cpp +++ b/SharedGraphics/PNGLoader.cpp @@ -9,6 +9,8 @@ #include <SharedGraphics/puff.c> #include <serenity.h> +//#define PNG_STOPWATCH_DEBUG + struct PNG_IHDR { NetworkOrdered<dword> width; NetworkOrdered<dword> height; @@ -246,7 +248,9 @@ template<bool has_alpha, byte filter_type> [[gnu::noinline]] static void unfilter(PNGLoadingContext& context) { { +#ifdef PNG_STOPWATCH_DEBUG Stopwatch sw("load_png_impl: unfilter: unpack"); +#endif // First unpack the scanlines to RGBA: switch (context.color_type) { case 2: @@ -275,7 +279,9 @@ template<bool has_alpha, byte filter_type> auto dummy_scanline = ByteBuffer::create_zeroed(context.width * sizeof(RGBA32)); +#ifdef PNG_STOPWATCH_DEBUG Stopwatch sw("load_png_impl: unfilter: process"); +#endif for (int y = 0; y < context.height; ++y) { auto filter = context.scanlines[y].filter; if (filter == 0) { @@ -318,7 +324,9 @@ template<bool has_alpha, byte filter_type> static RetainPtr<GraphicsBitmap> load_png_impl(const byte* data, int data_size) { +#ifdef PNG_STOPWATCH_DEBUG Stopwatch sw("load_png_impl: total"); +#endif const byte* data_ptr = data; int data_remaining = data_size; @@ -336,7 +344,9 @@ static RetainPtr<GraphicsBitmap> load_png_impl(const byte* data, int data_size) data_remaining -= sizeof(png_header); { +#ifdef PNG_STOPWATCH_DEBUG Stopwatch sw("load_png_impl: read chunks"); +#endif Streamer streamer(data_ptr, data_remaining); while (!streamer.at_end()) { if (!process_chunk(streamer, context)) { @@ -346,7 +356,9 @@ static RetainPtr<GraphicsBitmap> load_png_impl(const byte* data, int data_size) } { +#ifdef PNG_STOPWATCH_DEBUG Stopwatch sw("load_png_impl: uncompress"); +#endif unsigned long srclen = context.compressed_data.size() - 6; unsigned long destlen = context.decompression_buffer_size; int ret = puff(context.decompression_buffer, &destlen, context.compressed_data.data() + 2, &srclen); @@ -356,7 +368,9 @@ static RetainPtr<GraphicsBitmap> load_png_impl(const byte* data, int data_size) } { +#ifdef PNG_STOPWATCH_DEBUG Stopwatch sw("load_png_impl: extract scanlines"); +#endif context.scanlines.ensure_capacity(context.height); Streamer streamer(context.decompression_buffer, context.decompression_buffer_size); for (int y = 0; y < context.height; ++y) { @@ -372,7 +386,9 @@ static RetainPtr<GraphicsBitmap> load_png_impl(const byte* data, int data_size) } { +#ifdef PNG_STOPWATCH_DEBUG Stopwatch sw("load_png_impl: create bitmap"); +#endif context.bitmap = GraphicsBitmap::create(context.has_alpha() ? GraphicsBitmap::Format::RGBA32 : GraphicsBitmap::Format::RGB32, { context.width, context.height }); } |