mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 09:00:22 +00:00
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. :^)
This commit is contained in:
parent
b0de6aa8d8
commit
60d25f0f4a
Notes:
sideshowbarker
2024-07-19 14:57:58 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/60d25f0f4a7
32 changed files with 1356 additions and 1098 deletions
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "StdLibExtras.h"
|
||||
#include <AK/Assertions.h>
|
||||
|
||||
namespace AK {
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "DiskBackedFileSystem.h"
|
||||
#include "i386.h"
|
||||
#include <AK/InlineLRUCache.h>
|
||||
#include <Kernel/Process.h>
|
||||
|
||||
//#define DBFS_DEBUG
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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 };
|
||||
};
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ KERNEL_OBJS = \
|
|||
StdLib.o \
|
||||
i386.o \
|
||||
Process.o \
|
||||
Thread.o \
|
||||
i8253.o \
|
||||
KeyboardDevice.o \
|
||||
CMOS.o \
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -292,19 +292,24 @@ ByteBuffer procfs$pid_stack(InodeIdentifier identifier)
|
|||
dword address;
|
||||
const KSym* ksym;
|
||||
};
|
||||
StringBuilder builder;
|
||||
process.for_each_thread([&] (Thread& thread) {
|
||||
builder.appendf("Thread %d:\n", thread.tid());
|
||||
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) {
|
||||
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 });
|
||||
}
|
||||
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);
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
return builder.to_byte_buffer();
|
||||
}
|
||||
|
||||
|
@ -314,8 +319,10 @@ ByteBuffer procfs$pid_regs(InodeIdentifier identifier)
|
|||
if (!handle)
|
||||
return { };
|
||||
auto& process = handle->process();
|
||||
auto& tss = process.tss();
|
||||
StringBuilder builder;
|
||||
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);
|
||||
|
@ -327,6 +334,8 @@ ByteBuffer procfs$pid_regs(InodeIdentifier identifier)
|
|||
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())
|
||||
);
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load diff
221
Kernel/Process.h
221
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>
|
||||
|
@ -492,6 +377,21 @@ inline void Process::for_each_child(Callback callback)
|
|||
}
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
inline void Process::for_each_thread(Callback callback) const
|
||||
{
|
||||
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;
|
||||
}
|
||||
thread = next_thread;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
inline void Process::for_each_in_pgrp(pid_t pgid, Callback callback)
|
||||
{
|
||||
|
@ -506,30 +406,6 @@ inline void Process::for_each_in_pgrp(pid_t pgid, Callback callback)
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
inline void Process::for_each_living(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);
|
||||
process = next_process;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool InodeMetadata::may_read(Process& process) const
|
||||
{
|
||||
return may_read(process.euid(), process.gids());
|
||||
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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()));
|
||||
|
||||
if (current == &process)
|
||||
// FIXME(Thread): This is such a hack.
|
||||
if (&thread == &s_colonel_process->main_thread())
|
||||
thread.set_ticks_left(1);
|
||||
|
||||
thread.did_schedule();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
511
Kernel/Thread.cpp
Normal file
511
Kernel/Thread.cpp
Normal file
|
@ -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();
|
||||
}
|
205
Kernel/Thread.h
Normal file
205
Kernel/Thread.h
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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, ...)
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue