add page cache with read-ahead and dirty writeback, file-backed and anonymous mmap with CoW, VAD-based virtual memory replacing the old user region model, PFN database with eviction and reclaim, file identity for cross-open section sharing, FAT32 path cache and per-file cluster chain cache, serial.sys for COM1 keyboard input, xHCI MSI with INTx and polled fallback, PciEnableMsiAuto, waitable input queue, KeWaitForAnyEvent, per-process thread info syscall, expanded kernel stats (PFN/PTE/VAD/file-cache/disk), pool allocation tags, async I/O user buffer locking, file flush on close, add docs.md.
This commit is contained in:
1 parent
61ec2442eb
commit
2c4a68a75f
114 files changed
+18069
-2620
No files matched your search
+148
-20
@@ -6,7 +6,9 @@
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <kernel/ke_event.h>
|
||||
#include <kernel/ke_system_root.h>
|
||||
#include <kernel/ke_userenv.h>
|
||||
#include <kernel/io_file.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <kernel/ob_object.h>
|
||||
#include <kernel/ke_cpu.h>
|
||||
@@ -140,7 +142,7 @@ size_t KeCopyProcessInfo(struct SYS_PROCESS_INFO* infoOut, size_t maxCount) {
|
||||
if (p == system) info->Flags |= SYS_PROCESS_FLAG_SYSTEM;
|
||||
if (p == current) info->Flags |= SYS_PROCESS_FLAG_CURRENT;
|
||||
|
||||
MmQueryProcessUserMemory(p, &info->UserRegionCount,
|
||||
MmVadQueryProcessMemory(p, &info->VadCount,
|
||||
&info->UserReservedBytes,
|
||||
&info->UserCommittedBytes);
|
||||
|
||||
@@ -158,27 +160,128 @@ size_t KeCopyProcessInfo(struct SYS_PROCESS_INFO* infoOut, size_t maxCount) {
|
||||
return count;
|
||||
}
|
||||
|
||||
// Destructor (called by ObDereferenceObject when RefCount hits zero).
|
||||
static void KiProcessDestroy(OBJECT* obj) {
|
||||
PROCESS* proc = CONTAINING_RECORD(obj, PROCESS, Header);
|
||||
// Converts a kernel thread state to the public ABI state.
|
||||
static uint32_t KiThreadStateToSys(THREAD_STATE state) {
|
||||
switch (state) {
|
||||
case ThreadReady: return SYS_THREAD_STATE_READY;
|
||||
case ThreadRunning: return SYS_THREAD_STATE_RUNNING;
|
||||
case ThreadWaiting: return SYS_THREAD_STATE_WAITING;
|
||||
case ThreadDead: return SYS_THREAD_STATE_DEAD;
|
||||
default: return SYS_THREAD_STATE_DEAD;
|
||||
}
|
||||
}
|
||||
|
||||
if (proc == gSystemProcess)
|
||||
gSystemProcess = (PROCESS*)0;
|
||||
// Converts a kernel object type to the public wait-object type.
|
||||
static uint32_t KiWaitObjectTypeToSys(OBJECT_TYPE type) {
|
||||
switch (type) {
|
||||
case ObjectTypeProcess: return SYS_THREAD_WAIT_PROCESS;
|
||||
case ObjectTypeThread: return SYS_THREAD_WAIT_THREAD;
|
||||
case ObjectTypeMutex: return SYS_THREAD_WAIT_MUTEX;
|
||||
case ObjectTypeSemaphore: return SYS_THREAD_WAIT_SEMAPHORE;
|
||||
case ObjectTypeEvent: return SYS_THREAD_WAIT_EVENT;
|
||||
case ObjectTypeQueue: return SYS_THREAD_WAIT_QUEUE;
|
||||
case ObjectTypeFile: return SYS_THREAD_WAIT_FILE;
|
||||
case ObjectTypeDir: return SYS_THREAD_WAIT_DIR;
|
||||
case ObjectTypeAsyncIo: return SYS_THREAD_WAIT_ASYNC_IO;
|
||||
case ObjectTypeSocket: return SYS_THREAD_WAIT_SOCKET;
|
||||
case ObjectTypeDevice: return SYS_THREAD_WAIT_DEVICE;
|
||||
case ObjectTypeDriver: return SYS_THREAD_WAIT_DRIVER;
|
||||
case ObjectTypeUsbDevice: return SYS_THREAD_WAIT_USB_DEVICE;
|
||||
case ObjectTypeUsbPipe: return SYS_THREAD_WAIT_USB_PIPE;
|
||||
case ObjectTypeFileMapping: return SYS_THREAD_WAIT_FILE_MAPPING;
|
||||
case ObjectTypeSurface: return SYS_THREAD_WAIT_SURFACE;
|
||||
default: return SYS_THREAD_WAIT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
// Release all remaining handles.
|
||||
for (int i = 1; i < HANDLE_TABLE_SIZE; i++) {
|
||||
if (proc->Handles[i].Object) {
|
||||
ObDereferenceObject(proc->Handles[i].Object);
|
||||
// Copies one process's thread telemetry into caller-provided storage.
|
||||
KSTATUS KeCopyProcessThreadInfo(uint32_t pid, struct SYS_THREAD_INFO* infoOut, size_t maxCount, size_t* countOut) {
|
||||
if (!countOut) return STATUS_INVALID_PARAMETER;
|
||||
if (maxCount && !infoOut) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
PROCESS* process = KeGetProcessById(pid);
|
||||
if (!process) return STATUS_NOT_FOUND;
|
||||
|
||||
THREAD* current = KeGetCurrentThread();
|
||||
size_t count = 0;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&process->HandleLock);
|
||||
LIST_ENTRY* head = &process->ThreadList;
|
||||
for (LIST_ENTRY* node = head->Fwd; node != head; node = node->Fwd) {
|
||||
THREAD* thread = CONTAINING_RECORD(node, THREAD, ProcessLink);
|
||||
if (infoOut && count < maxCount) {
|
||||
SYS_THREAD_INFO* info = &infoOut[count];
|
||||
memset(info, 0, sizeof(*info));
|
||||
info->Size = sizeof(*info);
|
||||
info->ProcessId = process->ProcessId;
|
||||
info->ThreadId = thread->ThreadId;
|
||||
info->State = KiThreadStateToSys(thread->State);
|
||||
info->Priority = (uint32_t)thread->Priority;
|
||||
info->RunningCpu = thread->RunningCpu;
|
||||
info->LastCpu = thread->LastCpu;
|
||||
info->QuantumRemaining = thread->QuantumRemaining;
|
||||
info->QuantumTicks = thread->QuantumTicks;
|
||||
info->CpuBurstTicks = thread->CpuBurstTicks;
|
||||
info->LastRunTicks = thread->LastRunTicks;
|
||||
info->WaitObjectType = thread->WaitObject ? KiWaitObjectTypeToSys(thread->WaitObject->Type) : SYS_THREAD_WAIT_NONE;
|
||||
info->WaitStatus = (uint32_t)thread->WaitStatus;
|
||||
info->KernelApcDisable = thread->KernelApcDisable;
|
||||
|
||||
if (thread == current) info->Flags |= SYS_THREAD_FLAG_CURRENT;
|
||||
if (thread->IsIdleThread) info->Flags |= SYS_THREAD_FLAG_IDLE;
|
||||
if (!thread->Teb) info->Flags |= SYS_THREAD_FLAG_KERNEL;
|
||||
if (thread->Queued) info->Flags |= SYS_THREAD_FLAG_QUEUED;
|
||||
if (thread->AlertableWait) info->Flags |= SYS_THREAD_FLAG_ALERTABLE_WAIT;
|
||||
if (thread->KernelApcPending) info->Flags |= SYS_THREAD_FLAG_KERNEL_APC_PENDING;
|
||||
if (thread->UserApcPending) info->Flags |= SYS_THREAD_FLAG_USER_APC_PENDING;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
KeReleaseSpinlock(&process->HandleLock, flags);
|
||||
|
||||
*countOut = count;
|
||||
ObDereferenceObject(&process->Header);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Releases all kernel objects still reachable from a process handle table.
|
||||
// This is part of process exit, not merely object destruction: a parent may keep
|
||||
// a handle to an exited process for wait/query, but the dead process must not
|
||||
// keep its own files, mappings, queues, sockets, or synchronization objects alive.
|
||||
static void KiProcessReleaseAllHandles(PROCESS* proc) {
|
||||
if (!proc) return;
|
||||
|
||||
for (uint32_t i = 1; i < HANDLE_TABLE_SIZE; ++i) {
|
||||
OBJECT* obj = (OBJECT*)0;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&proc->HandleLock);
|
||||
obj = proc->Handles[i].Object;
|
||||
if (obj) {
|
||||
proc->Handles[i].Object = (OBJECT*)0;
|
||||
proc->Handles[i].Rights = 0;
|
||||
proc->Handles[i].Flags = 0;
|
||||
}
|
||||
KeReleaseSpinlock(&proc->HandleLock, flags);
|
||||
|
||||
if (obj)
|
||||
ObDereferenceObject(obj);
|
||||
}
|
||||
}
|
||||
|
||||
// Releases the process-owned address space and per-process kernel resources.
|
||||
// Idempotent because the normal exit path and the final OBJECT destructor can
|
||||
// both observe a process whose threads are already gone.
|
||||
static void KiProcessRundownResources(PROCESS* proc) {
|
||||
if (!proc) return;
|
||||
if (_InterlockedExchange(&proc->ResourcesReleased, 1) != 0)
|
||||
return;
|
||||
|
||||
KiProcessReleaseAllHandles(proc);
|
||||
|
||||
// Release every user VA region owned by this process. This includes the
|
||||
// main image, DLLs, TEBs/stacks that survived thread teardown, process
|
||||
// environment strings, and leaked user allocations.
|
||||
MmDestroyAllUserRegions(proc);
|
||||
// environment strings, mmap views, and leaked user allocations.
|
||||
MmVadDestroyAllForProcess(proc);
|
||||
proc->ImageBase = 0;
|
||||
proc->ImageSize = 0;
|
||||
proc->ModuleCount = 0;
|
||||
@@ -190,8 +293,21 @@ static void KiProcessDestroy(OBJECT* obj) {
|
||||
proc->UserCmdlineSize = 0;
|
||||
proc->UserCwdBase = 0;
|
||||
proc->UserCwdSize = 0;
|
||||
}
|
||||
|
||||
// Release the exit event.
|
||||
// Destructor (called by ObDereferenceObject when RefCount hits zero).
|
||||
static void KiProcessDestroy(OBJECT* obj) {
|
||||
PROCESS* proc = CONTAINING_RECORD(obj, PROCESS, Header);
|
||||
|
||||
if (proc == gSystemProcess)
|
||||
gSystemProcess = (PROCESS*)0;
|
||||
|
||||
// If the process failed during launch, or no normal exit completion path
|
||||
// ran, make sure its own handles and VADs are still reclaimed.
|
||||
KiProcessRundownResources(proc);
|
||||
|
||||
// Release the exit event only when the process object itself dies. External
|
||||
// process handles may still wait/query an exited process after rundown.
|
||||
if (proc->ExitEvent) {
|
||||
ObDereferenceObject(&proc->ExitEvent->Header);
|
||||
proc->ExitEvent = (EVENT *)0;
|
||||
@@ -227,6 +343,7 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
|
||||
proc->ExitCode = 0;
|
||||
proc->Terminating = 0;
|
||||
proc->Exited = 0;
|
||||
proc->ResourcesReleased = 0;
|
||||
proc->ImageBase = 0;
|
||||
proc->ImageSize = 0;
|
||||
proc->UserPebBase = 0;
|
||||
@@ -240,10 +357,10 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
|
||||
proc->ModuleCount = 0;
|
||||
proc->CurrentDirectory[0] = '\0';
|
||||
proc->CurrentDirectoryLength = 0;
|
||||
proc->CurrentDrive = 0;
|
||||
proc->CurrentDrive = KeGetSystemRootDrive();
|
||||
proc->CwdLock = (SPINLOCK)SPINLOCK_INIT;
|
||||
LIST_INIT(&proc->UserRegions);
|
||||
proc->UserRegionLock = (SPINLOCK)SPINLOCK_INIT;
|
||||
LIST_INIT(&proc->VadList);
|
||||
proc->VadLock = (SPINLOCK)SPINLOCK_INIT;
|
||||
|
||||
// Slot 0 is the invalid handle - leave it NULL.
|
||||
memset(proc->Handles, 0, sizeof(proc->Handles));
|
||||
@@ -427,7 +544,7 @@ fail:
|
||||
}
|
||||
|
||||
// Process exit / termination.
|
||||
static void KiCompleteProcessExitIfLastThread(PROCESS* process) {
|
||||
void KeCompleteProcessExitIfLastThread(PROCESS* process) {
|
||||
if (!process) return;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&process->HandleLock);
|
||||
@@ -435,6 +552,7 @@ static void KiCompleteProcessExitIfLastThread(PROCESS* process) {
|
||||
KeReleaseSpinlock(&process->HandleLock, flags);
|
||||
|
||||
if (threadCount == 0 && _InterlockedExchange(&process->Exited, 1) == 0) {
|
||||
KiProcessRundownResources(process);
|
||||
if (process->ExitEvent) KeSignalEvent(process->ExitEvent);
|
||||
}
|
||||
}
|
||||
@@ -448,7 +566,7 @@ void KeExitProcess(PROCESS* process, int exitCode) {
|
||||
// The calling thread exits immediately after this in SysExitProcess. Do
|
||||
// not drop the process object here: each thread owns one process reference,
|
||||
// and KiThreadDestroy releases that reference after unlinking the thread.
|
||||
KiCompleteProcessExitIfLastThread(process);
|
||||
KeCompleteProcessExitIfLastThread(process);
|
||||
}
|
||||
|
||||
// Implements the terminate Process operation.
|
||||
@@ -469,7 +587,15 @@ KSTATUS KeTerminateProcess(PROCESS* process, int exitCode) {
|
||||
if (t == self)
|
||||
continue;
|
||||
|
||||
(void)KeAlertThread(t);
|
||||
// A terminating process must not be held open by worker threads that
|
||||
// are blocked in non-alertable waits. User-mode DLLs such as Bink can
|
||||
// leave IO/submit workers parked on semaphores; after the foreground
|
||||
// thread crashes, those waiters would otherwise keep ThreadCount > 0
|
||||
// and prevent the process ExitEvent from waking the shell.
|
||||
if (t->State == ThreadWaiting)
|
||||
KeAbortThreadWait(t, STATUS_ALERTED);
|
||||
else
|
||||
(void)KeAlertThread(t);
|
||||
|
||||
if (t->RunningCpu != KE_INVALID_CPU && t->RunningCpu != selfIndex && t->RunningCpu < gCpuCount) {
|
||||
KCPU* cpu = &gCpus[t->RunningCpu];
|
||||
@@ -481,7 +607,7 @@ KSTATUS KeTerminateProcess(PROCESS* process, int exitCode) {
|
||||
}
|
||||
KeReleaseSpinlock(&process->HandleLock, flags);
|
||||
|
||||
KiCompleteProcessExitIfLastThread(process);
|
||||
KeCompleteProcessExitIfLastThread(process);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -535,6 +661,8 @@ void KeCloseHandle(PROCESS* process, uint32_t handle) {
|
||||
process->Handles[handle].Rights = 0;
|
||||
process->Handles[handle].Flags = 0;
|
||||
KeReleaseSpinlock(&process->HandleLock, flags);
|
||||
if (obj->Type == ObjectTypeFile)
|
||||
(void)IoFlushFile((FILE_OBJECT*)obj);
|
||||
ObDereferenceObject(obj);
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user