diff --git a/user/alarmdefault.c b/user/alarmdefault.c new file mode 100644 index 0000000..5605e8b --- /dev/null +++ b/user/alarmdefault.c @@ -0,0 +1,53 @@ +#include "ulib.h" + +/* + * Only ever spawned by alarmtest.c. Installs NO sigAlrm handler at all -- + * the real-Unix default disposition for SIGALRM (signal.c's + * sigDefaultIsIgnore explicitly does NOT list sigAlrm, so it falls through + * to the same default-Terminate path every other "real" signal in this + * kernel already has) is Terminate, not Ignore. userAlarm(1) schedules a + * real SIGALRM one second from now against THIS process; the read below + * blocks on a pipe no writer will ever attach to (the same "blocked + * forever" setup eintrtarget.c/killvictim.c already use), so the only way + * this process ever leaves that read is the alarm actually firing and + * killing it -- exactly like eintrtarget.c, except the interrupting signal + * here comes from schedule()'s own timer-tick expiry scan (sched.c), not + * from another process's userSigSend. If threadSetAlarm/schedule's expiry + * scan were ever broken, this process would simply never return from the + * read below, hanging the whole boot gate to its 25s QEMU timeout instead + * of merely failing a check -- the same fault-injection-visible shape + * eintrtest.c's own file comment already establishes for early-wake. + */ + +int main(void) +{ + print("alarmdefault: start, pid="); + printDec(userGetpid()); + print("\n"); + + long victimHandle = userPipeCreate(); + if (victimHandle < 0) { + print("alarmdefault: FAIL create victim pipe\n"); + return 1; + } + long victimFd = userPipeAttach(victimHandle, 0); + if (victimFd < 0) { + print("alarmdefault: FAIL attach victim pipe\n"); + return 1; + } + + if (userAlarm(1) != 0) { + print("alarmdefault: FAIL unexpected pre-existing alarm\n"); + return 1; + } + + print("alarmdefault: blocking on read forever\n"); + char buffer[4]; + userRead((int)victimFd, buffer, sizeof(buffer)); + + /* Only reached if userRead somehow returned on its own -- nothing ever + writes to the victim pipe, so the alarm not firing (or firing but + not actually terminating this process) is the only way to get here. */ + print("alarmdefault: FAIL read returned without being signaled\n"); + return 1; +} diff --git a/user/argecho.c b/user/argecho.c new file mode 100644 index 0000000..c1fac0c --- /dev/null +++ b/user/argecho.c @@ -0,0 +1,24 @@ +#include "ulib.h" + +/* + * Real ring-3 proof that shell.c's `$NAME`/`$?` expansion (shellExpandArgv, + * shellExpandToken) genuinely reaches a SPAWNED program's own argv, not + * just a shell builtin's -- every earlier consumer of expansion (export, + * env, echo) is a builtin, and shell.c's doEcho never calls shellExpandArgv + * at all (only runProgram/runPipelineStages do, for a real spawned + * process), so nothing in this kernel's live-demo chain had ever actually + * typed a `$` at the real shell before this program existed. Prints each + * argv[1..argc) on its own line, prefixed like globcat.c's own + * "globcat: " diagnostic style -- a generic argv printer, not tied to any + * one variable, so a future demo can reuse it for any other `$NAME`/`$?` + * case without needing yet another single-purpose binary. + */ +int main(int argc, char** argv) +{ + for (int i = 1; i < argc; i++) { + print("argecho: "); + print(argv[i]); + print("\n"); + } + return 0; +} diff --git a/user/consolereader1.c b/user/consolereader1.c new file mode 100644 index 0000000..cb9e109 --- /dev/null +++ b/user/consolereader1.c @@ -0,0 +1,37 @@ +#include "ulib.h" + +/* + * Boot-spawned independently of consolereader2.c (see kmain.c's spawnUser + * calls) -- like namedreader.c/namedwriter.c, neither process's pid, argv, + * or fd table ever mentions the other. Both simply read fd 0, which every + * process gets for free (vfs.c's vfsFdInit) as its own console reader queue + * (keyboard.h) rather than a single kernel-wide buffer: proving that two + * completely unrelated processes reading "the keyboard" each get their own + * independent copy of every keystroke instead of racing each other for it. + * + * kmain.c's consoleInjectorThread fires exactly one synthetic keystroke a + * few hundred milliseconds after boot (there is no automated way to drive a + * real PS/2 device under headless QEMU, see its own comment) through the + * same keyboardHandleScancode the real IRQ handler calls -- so this process + * has to genuinely park in userRead across many timer ticks and be woken + * later, not just win a race against a byte that was already queued. + */ + +int main(void) +{ + print("consolereader1: start, pid="); + printDec(userGetpid()); + print(", blocking on fd 0\n"); + + char c = 0; + long got = userRead(0, &c, 1); + if (got != 1) { + print("consolereader1: FAIL read\n"); + return 1; + } + + print("consolereader1: got byte '"); + userWrite(1, &c, 1); + print("' -> console-fanout-1-ok\n"); + return 0; +} diff --git a/user/consolereader2.c b/user/consolereader2.c new file mode 100644 index 0000000..0c194d0 --- /dev/null +++ b/user/consolereader2.c @@ -0,0 +1,30 @@ +#include "ulib.h" + +/* See consolereader1.c's comment -- an entirely independent process reading + the same fd 0, spawned separately by kmain.c, sharing nothing with + consolereader1 but the fact that both happen to be alive when + consoleInjectorThread's single synthetic keystroke fires. If fan-out + ever regressed back to one shared buffer, whichever of the two processes + the scheduler happened to run first would steal the only queued byte and + the other would hang forever -- the boot gate's timeout would catch that + as a missing needle rather than a hang, since only this process is + missing, not the whole machine. */ + +int main(void) +{ + print("consolereader2: start, pid="); + printDec(userGetpid()); + print(", blocking on fd 0\n"); + + char c = 0; + long got = userRead(0, &c, 1); + if (got != 1) { + print("consolereader2: FAIL read\n"); + return 1; + } + + print("consolereader2: got byte '"); + userWrite(1, &c, 1); + print("' -> console-fanout-2-ok\n"); + return 0; +} diff --git a/user/crt.S b/user/crt.S index 600fdf3..2a74282 100644 --- a/user/crt.S +++ b/user/crt.S @@ -2,6 +2,11 @@ .global _start _start: xorq %rbp, %rbp + /* Real x86_64 SysV process entry: rsp points at argc, with argv[0..] + and a NULL terminator right above it (elfBuildInitialStack, + elf.c/syscall.c doExec/doSpawn, kmain.c spawnUser). */ + movq (%rsp), %rdi + leaq 8(%rsp), %rsi call main movq %rax, %rdi movq $0, %rax