feat: process exit codes, posix_spawn, 4 KiB command lines
Groundwork for the GCC driver: a compiler driver must spawn cc1/as/ld and know whether each stage succeeded. Kernel: the scheduler keeps an exit-code ledger (pid -> code; pids are monotonic so entries never alias), published during teardown right before waiters wake. SYS_EXIT records main()'s return value, SYS_KILL records 256+SIGKILL, and the exception handler records 256+signal mapped from the fault vector (#PF/#GP -> SIGSEGV, #DE/FP -> SIGFPE, #UD -> SIGILL). SYS_WAITPID now returns the code: 0..255 for a normal exit, 256+signal for a violent death. Process args grow from 256 bytes to 4 KiB (cc1 invocations do not fit in 256), with crt1 now parsing up to 255 argv entries from a static buffer. libc: new spawn.h with posix_spawn/posix_spawnp over SYS_SPAWN - libiberty's pex layer has a posix_spawn backend, so GCC's driver works without fork. argv is joined into the kernel args string (spaces in arguments rejected; no kernel quoting), envp is not transferred, and non-empty file actions fail loudly with ENOTSUP until the kernel can redirect stdio on spawn. waitpid() now decodes real POSIX status and the sys/wait.h macros distinguish exited from signaled children. Shell: prints [exit code N] after nonzero exits and [terminated by signal N] for killed or crashed children. Verified on the OS: cat on a missing file reports exit code 1; a window-close (clean exit 0) stays silent as it should. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
@@ -12,4 +12,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define MONTAUK_BUILD_NUMBER 13
|
||||
#define MONTAUK_BUILD_NUMBER 15
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
namespace montauk::abi {
|
||||
static void Sys_Exit(int exitCode) {
|
||||
(void)exitCode;
|
||||
Sched::SetProcessExitCode(Sched::GetCurrentPid(), exitCode & 0xFF);
|
||||
Sched::ExitProcess();
|
||||
}
|
||||
|
||||
@@ -36,8 +36,11 @@ namespace montauk::abi {
|
||||
return Sched::GetCurrentPid();
|
||||
}
|
||||
|
||||
static void Sys_WaitPid(int pid) {
|
||||
// Returns 0..255 for a normal exit, 256+signal for killed/crashed,
|
||||
// 0 when the pid is unknown or its ledger entry has been evicted.
|
||||
static int Sys_WaitPid(int pid) {
|
||||
Sched::BlockOnPid(pid);
|
||||
return Sched::LookupExitCode(pid);
|
||||
}
|
||||
|
||||
static int Sys_Spawn(const char* path, const char* args) {
|
||||
|
||||
@@ -47,7 +47,8 @@ namespace montauk::abi {
|
||||
|
||||
static constexpr uint64_t kMaxPrintableStringBytes = 4096;
|
||||
static constexpr uint64_t kMaxPathBytes = 256;
|
||||
static constexpr uint64_t kMaxArgsBytes = 256;
|
||||
// Large enough for compiler driver command lines (cc1/ld invocations).
|
||||
static constexpr uint64_t kMaxArgsBytes = 4096;
|
||||
static constexpr uint64_t kMaxWindowTitleBytes = 256;
|
||||
static constexpr uint64_t kMaxHostnameBytes = 256;
|
||||
static constexpr uint64_t kMaxUserNameBytes = 32;
|
||||
@@ -140,8 +141,7 @@ namespace montauk::abi {
|
||||
return (int64_t)Sys_Spawn((const char*)frame->arg1,
|
||||
UserMemory::IsUserPtr(frame->arg2) ? (const char*)frame->arg2 : nullptr);
|
||||
case SYS_WAITPID:
|
||||
Sys_WaitPid((int)frame->arg1);
|
||||
return 0;
|
||||
return Sys_WaitPid((int)frame->arg1);
|
||||
case SYS_FBINFO:
|
||||
if (!UserMemory::Writable<FbInfo>(frame->arg1)) return -1;
|
||||
Sys_FbInfo((FbInfo*)frame->arg1);
|
||||
|
||||
@@ -143,6 +143,16 @@ namespace Hal {
|
||||
CrashReport::AddReport(rep);
|
||||
Sched::SpawnCrashPad(proc->pid);
|
||||
|
||||
// Record a killed-by-signal exit code (256+sig) so a parent
|
||||
// blocked in SYS_WAITPID can tell a crash from a clean exit.
|
||||
{
|
||||
int sig = 11; /* SIGSEGV */
|
||||
if (i == 0x00) sig = 8; /* #DE -> SIGFPE */
|
||||
else if (i == 0x06) sig = 4; /* #UD -> SIGILL */
|
||||
else if (i == 0x10 || i == 0x13) sig = 8; /* x87/SIMD FP */
|
||||
Sched::SetProcessExitCode(proc->pid, 256 + sig);
|
||||
}
|
||||
|
||||
Sched::ExitProcess();
|
||||
__builtin_unreachable();
|
||||
} else {
|
||||
|
||||
@@ -43,6 +43,15 @@ namespace Sched {
|
||||
static Process processTable[MaxProcesses];
|
||||
static int nextPid = 0;
|
||||
|
||||
// Exit-code ledger. Pids are monotonic and never recycled, so entries
|
||||
// cannot alias. Written under schedLock during process teardown; read
|
||||
// by SYS_WAITPID after the waiter unblocks. Codes: 0..255 = normal
|
||||
// exit status, 256+signal = killed or crashed.
|
||||
static constexpr int ExitLedgerSize = 128;
|
||||
static int exitLedgerPid[ExitLedgerSize];
|
||||
static int exitLedgerCode[ExitLedgerSize];
|
||||
static int exitLedgerHead = 0;
|
||||
|
||||
// The scheduler lock MUST be a Spinlock (interrupt-disabling).
|
||||
// It is held ACROSS context switches to prevent the race where
|
||||
// another CPU picks up a process whose RSP hasn't been saved yet.
|
||||
@@ -212,6 +221,11 @@ namespace Sched {
|
||||
}
|
||||
|
||||
nextPid = 0;
|
||||
for (int i = 0; i < ExitLedgerSize; i++) {
|
||||
exitLedgerPid[i] = -1;
|
||||
exitLedgerCode[i] = 0;
|
||||
}
|
||||
exitLedgerHead = 0;
|
||||
Hal::RegisterIrqHandler(Hal::IRQ_RESCHEDULE, RescheduleIpiHandler);
|
||||
|
||||
Kt::KernelLogStream(Kt::OK, "Sched") << "Initialized (" << MaxProcesses
|
||||
@@ -389,7 +403,7 @@ namespace Sched {
|
||||
proc.args[0] = '\0';
|
||||
if (args != nullptr) {
|
||||
int i = 0;
|
||||
for (; i < 255 && args[i]; i++) {
|
||||
for (; i < 4095 && args[i]; i++) {
|
||||
proc.args[i] = args[i];
|
||||
}
|
||||
proc.args[i] = '\0';
|
||||
@@ -1152,6 +1166,12 @@ namespace Sched {
|
||||
proc.runningOnCpu = -1;
|
||||
proc.reapReady = true;
|
||||
|
||||
// Publish the exit code before waking waiters so SYS_WAITPID
|
||||
// observes it the moment it unblocks.
|
||||
exitLedgerPid[exitLedgerHead] = exitingPid;
|
||||
exitLedgerCode[exitLedgerHead] = proc.exitCode;
|
||||
exitLedgerHead = (exitLedgerHead + 1) % ExitLedgerSize;
|
||||
|
||||
// Wake any processes blocked on this PID
|
||||
for (int i = 0; i < MaxProcesses; i++) {
|
||||
if (processTable[i].state == ProcessState::Blocked &&
|
||||
@@ -1232,6 +1252,8 @@ namespace Sched {
|
||||
return -1;
|
||||
}
|
||||
|
||||
processTable[primarySlot_].exitCode = 256 + 9; /* killed (SIGKILL) */
|
||||
|
||||
// Flag the main thread so its next tick (or scheduler dispatch)
|
||||
// routes through ExitProcess, which sweeps every sibling thread
|
||||
// and tears down the address space. If the main thread is parked
|
||||
@@ -1263,6 +1285,33 @@ namespace Sched {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LookupExitCode(int pid) {
|
||||
schedLock.Acquire();
|
||||
int code = 0;
|
||||
for (int i = 0; i < ExitLedgerSize; i++) {
|
||||
if (exitLedgerPid[i] == pid) {
|
||||
code = exitLedgerCode[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
schedLock.Release();
|
||||
return code;
|
||||
}
|
||||
|
||||
void SetProcessExitCode(int pid, int code) {
|
||||
schedLock.Acquire();
|
||||
for (int i = 0; i < MaxProcesses; i++) {
|
||||
if (processTable[i].pid != pid) continue;
|
||||
// Only a process (primary slot) carries a process exit code;
|
||||
// sibling-thread exit values go through the join path.
|
||||
if (processTable[i].primarySlot == i) {
|
||||
processTable[i].exitCode = code;
|
||||
}
|
||||
break;
|
||||
}
|
||||
schedLock.Release();
|
||||
}
|
||||
|
||||
void BlockOnPid(int pid) {
|
||||
// If the target is already dead, return immediately
|
||||
if (!IsAlive(pid)) return;
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace Sched {
|
||||
uint64_t userStackTop; // User-space stack top
|
||||
uint64_t heapNext; // Simple bump allocator for user heap
|
||||
uint32_t readdirCursor; // Next SYS_READDIR scratch slot
|
||||
char args[256]; // Command-line arguments (set by parent via Spawn)
|
||||
char args[4096]; // Command-line arguments (set by parent via Spawn)
|
||||
char user[32]; // Owner user name (inherited from parent on spawn)
|
||||
char cwd[256]; // Absolute current working directory
|
||||
|
||||
@@ -158,6 +158,8 @@ namespace Sched {
|
||||
|
||||
// Block the current process until the given PID exits.
|
||||
void BlockOnPid(int pid);
|
||||
int LookupExitCode(int pid);
|
||||
void SetProcessExitCode(int pid, int code);
|
||||
|
||||
// Block the current process for the given number of milliseconds.
|
||||
void BlockForSleep(uint64_t ms);
|
||||
|
||||
Reference in New Issue
Block a user