feat: extend kmain with execReaperThread and consoleInjectorThread
This commit is contained in:
1 parent
661ff83253
commit
b4f8d5215f
1 file changed
+192
-2
+192
-2
@@ -307,13 +307,203 @@ static uint32 spawnUser(const char* path, const char* name, uint64 stackTop, uin
|
|||||||
return threadInvalidId;
|
return threadInvalidId;
|
||||||
}
|
}
|
||||||
|
|
||||||
pagingMapUserRange(stackTop - userStackSize, userStackSize);
|
pagingMapUserRangeFlags(stackTop - userStackSize, userStackSize, pageNoExecute);
|
||||||
uint32 pid = threadCreateUser(name, entry, stackTop);
|
|
||||||
|
/* Every boot-launched program gets a real, if minimal, argv: just its
|
||||||
|
own path, matching what a real OS would put in argv[0]. Nothing in
|
||||||
|
this kernel's boot sequence needs more than that -- programs wanting
|
||||||
|
specific arguments are spawned at runtime instead (doSpawn,
|
||||||
|
syscall.c), where the caller controls argv directly. */
|
||||||
|
const char* argv[1] = { path };
|
||||||
|
uint64 initialRsp;
|
||||||
|
if (!elfBuildInitialStack(stackTop, userStackSize, argv, 1, &initialRsp)) {
|
||||||
|
kprintf("[user] '%s' not started: argv did not fit its stack\n", path);
|
||||||
|
return threadInvalidId;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 pid = threadCreateUser(name, entry, initialRsp);
|
||||||
|
if (pid == threadInvalidId) {
|
||||||
|
kprintf("[user] '%s' not started: thread table full\n", path);
|
||||||
|
return threadInvalidId;
|
||||||
|
}
|
||||||
if (brkStart)
|
if (brkStart)
|
||||||
threadSetBrk(pid, brkStart);
|
threadSetBrk(pid, brkStart);
|
||||||
|
|
||||||
kprintf("[user] loaded '%s' pid=%u entry=%p stackTop=%p (%lu bytes)\n",
|
kprintf("[user] loaded '%s' pid=%u entry=%p stackTop=%p (%lu bytes)\n",
|
||||||
name, pid, (void*)entry, (void*)stackTop, size);
|
name, pid, (void*)entry, (void*)stackTop, size);
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* proctest.c's very last action on its success path is userExec(), which
|
||||||
|
replaces its own image with exectarget.c's -- so the only way to observe
|
||||||
|
from OUTSIDE that process that exec() genuinely happened (rather than
|
||||||
|
proctest simply exiting cleanly on its own) is to threadJoin() the exact
|
||||||
|
pid kmain.c originally gave proctest and check that its eventual exit
|
||||||
|
code is exectarget's (7), not proctest's own success path (which never
|
||||||
|
returns 7 -- it either exits 1 on a self-detected FAIL or never exits at
|
||||||
|
all once exec succeeds). A mismatch or an exit code belonging to neither
|
||||||
|
program both surface as a plain FAIL below. */
|
||||||
|
#define execDemoExitCode 7ull
|
||||||
|
|
||||||
|
static uint32 execDemoPid = threadInvalidId;
|
||||||
|
|
||||||
|
static void execReaperThread(void)
|
||||||
|
{
|
||||||
|
if (execDemoPid == threadInvalidId) {
|
||||||
|
kprintf("[user] exec demo skipped: proctest never started\n");
|
||||||
|
for (;;)
|
||||||
|
halt();
|
||||||
|
}
|
||||||
|
|
||||||
|
int64 code = threadJoin(execDemoPid);
|
||||||
|
bool ok = (code == (int64)execDemoExitCode);
|
||||||
|
kprintf("[user] exec demo: pid=%u final exitCode=%ld (expected %lu) -> %s\n",
|
||||||
|
execDemoPid, code, execDemoExitCode, ok ? "exec-pid-preserved-ok" : "exec-pid-preserved-FAIL");
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
halt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Headless QEMU has no automated way to drive a real PS/2 keyboard device --
|
||||||
|
ci-test.sh only captures the COM1 serial log, and scripts no QEMU monitor
|
||||||
|
"sendkey" command -- so this thread simulates exactly one keystroke the
|
||||||
|
same way a real IRQ1 handler would: by calling keyboardHandleScancode
|
||||||
|
(keyboard.h) directly with the scancode a real 'k' keypress generates.
|
||||||
|
consolereader1.c and consolereader2.c are both already parked in a real,
|
||||||
|
blocking userRead(0, ...) well before this fires (the sleep below gives
|
||||||
|
every boot-time thread, including both of them, at least one CFS-lite
|
||||||
|
timeslice first) -- keyboard.c's per-reader fan-out then delivers each of
|
||||||
|
them its own independent copy of the same byte, proving two entirely
|
||||||
|
unrelated ring-3 processes can share "the keyboard" without racing each
|
||||||
|
other for it, the gap the previous wave's own notes called out as the
|
||||||
|
next logical step after pipes. */
|
||||||
|
#define consoleDemoScancodeK 0x25
|
||||||
|
#define consoleInjectDelayMs 250
|
||||||
|
|
||||||
|
static void consoleInjectorThread(void)
|
||||||
|
{
|
||||||
|
threadSleep(consoleInjectDelayMs);
|
||||||
|
while (currentThread()->state == threadSleeping)
|
||||||
|
halt();
|
||||||
|
|
||||||
|
kprintf("[kbd] injecting synthetic keypress 'k' for the console fan-out demo\n");
|
||||||
|
keyboardHandleScancode(consoleDemoScancodeK);
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
halt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Same "headless QEMU cannot drive a real PS/2 device" reasoning as
|
||||||
|
consoleInjectorThread above, this time simulating a real Ctrl-C
|
||||||
|
keypress (left-Ctrl held, 'c' pressed and released, left-Ctrl released)
|
||||||
|
by calling keyboardHandleScancode with exactly the four scancodes a
|
||||||
|
physical keyboard would generate for it -- the live ring-3 proof that
|
||||||
|
sched.h's new Thread.pgid and keyboard.c's own new INTR interception
|
||||||
|
actually scope a terminal-generated signal to ONE process group
|
||||||
|
(pgroupfg.c, made the foreground below) and leave a completely
|
||||||
|
different one (pgroupbg.c, deliberately left as its own default group)
|
||||||
|
untouched. pgroupfgPid/pgroupbgPid are threadInvalidId if either
|
||||||
|
program failed to load (thread table full, missing from the ramdisk) --
|
||||||
|
this thread degrades to a silent no-op rather than acting on a bogus id,
|
||||||
|
the same "skipped" pattern execReaperThread already uses for its own
|
||||||
|
demo. */
|
||||||
|
#define scancodeLeftCtrlMake 0x1D
|
||||||
|
#define scancodeLeftCtrlBreak 0x9D
|
||||||
|
#define scancodeCPressMake 0x2E
|
||||||
|
#define scancodeCPressBreak 0xAE
|
||||||
|
#define pgroupInjectDelayMs 400
|
||||||
|
#define pgroupSettleDelayMs 100
|
||||||
|
|
||||||
|
static uint32 pgroupfgPid = threadInvalidId;
|
||||||
|
static uint32 pgroupbgPid = threadInvalidId;
|
||||||
|
|
||||||
|
static void pgroupInjectorThread(void)
|
||||||
|
{
|
||||||
|
if (pgroupfgPid == threadInvalidId || pgroupbgPid == threadInvalidId) {
|
||||||
|
kprintf("[kbd] process-group demo skipped: pgroupfg/pgroupbg never started\n");
|
||||||
|
for (;;)
|
||||||
|
halt();
|
||||||
|
}
|
||||||
|
|
||||||
|
threadSleep(pgroupInjectDelayMs);
|
||||||
|
while (currentThread()->state == threadSleeping)
|
||||||
|
halt();
|
||||||
|
|
||||||
|
kprintf("[kbd] injecting synthetic ctrl-c for the process-group demo\n");
|
||||||
|
keyboardHandleScancode(scancodeLeftCtrlMake);
|
||||||
|
keyboardHandleScancode(scancodeCPressMake);
|
||||||
|
keyboardHandleScancode(scancodeCPressBreak);
|
||||||
|
keyboardHandleScancode(scancodeLeftCtrlBreak);
|
||||||
|
|
||||||
|
/* Give the scheduler a real timeslice to actually deliver the
|
||||||
|
resulting sigInt to pgroupfg (early-wake + return-to-ring3, not
|
||||||
|
instantaneous) before this thread moves on to check anything. */
|
||||||
|
threadSleep(pgroupSettleDelayMs);
|
||||||
|
while (currentThread()->state == threadSleeping)
|
||||||
|
halt();
|
||||||
|
|
||||||
|
int64 fgCode = threadJoin(pgroupfgPid);
|
||||||
|
bool fgOk = (fgCode == (int64)(128 + sigInt));
|
||||||
|
kprintf("[user] pgroup demo: fg pid=%u final exitCode=%ld (expected 128+sigInt=%d) -> %s\n",
|
||||||
|
pgroupfgPid, fgCode, 128 + sigInt, fgOk ? "pgrouptest-fg-terminated-by-sigint-ok" : "FAIL");
|
||||||
|
|
||||||
|
/* Proves pgroupbg (a DIFFERENT process group, never named as
|
||||||
|
foreground) is still genuinely alive and running real code after
|
||||||
|
the Ctrl-C above -- if keyboard.c's group scoping were ever broken
|
||||||
|
to reach it too, pgroupbg would already be Dead by now and this
|
||||||
|
sigRaise (signal.h) would silently do nothing, so pgroupbg.c's own
|
||||||
|
"still-alive-after-ctrlc-ok" needle would simply never appear in
|
||||||
|
the boot log, failing the gate. */
|
||||||
|
sigRaise(pgroupbgPid, sigUsr1);
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
halt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The live ring-3 proof for shell.c's new `NAME [ARGS] [&]` job control
|
||||||
|
* (jobctl.h does the actual spawning/terminal-ownership work): rather than
|
||||||
|
* spawning jobctlfg.c/jobctlbg.c directly the way every demo above does,
|
||||||
|
* this thread TYPES commands at the real interactive shell (shellRun,
|
||||||
|
* already running as its own boot-time thread) through the same
|
||||||
|
* injected-keystroke mechanism consoleInjectorThread/pgroupInjectorThread
|
||||||
|
* above already use for the same "no automated way to drive a real PS/2
|
||||||
|
* device under headless QEMU" reason -- proving the shell's own runtime
|
||||||
|
* decision of what to load and whether to grant it the terminal, not just
|
||||||
|
* that jobctl.c's functions work when called directly.
|
||||||
|
*
|
||||||
|
* Scancode values below are read straight off keyboard.c's own
|
||||||
|
* scancodeLower table (index N there is scancode N here -- already cross-
|
||||||
|
* checked against that file's own worked examples: index 0x25 is 'k',
|
||||||
|
* exactly consoleDemoScancodeK above; index 0x2E is 'c', exactly
|
||||||
|
* scancodeCPressMake above), not guessed.
|
||||||
|
*/
|
||||||
|
#define scancodeMakeJ 0x24
|
||||||
|
#define scancodeMakeO 0x18
|
||||||
|
#define scancodeMakeB 0x30
|
||||||
|
#define scancodeMakeT 0x14
|
||||||
|
#define scancodeMakeL 0x26
|
||||||
|
#define scancodeMakeG 0x22
|
||||||
|
#define scancodeMakeF 0x21
|
||||||
|
#define scancodeMakeSpace 0x39
|
||||||
|
#define scancodeMakeSeven 0x08
|
||||||
|
#define scancodeMakeEnter 0x1C
|
||||||
|
#define scancodeLeftShiftMake 0x2A
|
||||||
|
#define scancodeLeftShiftBreak 0xAA
|
||||||
|
#define scancodeBreakBit 0x80
|
||||||
|
|
||||||
|
/* Additional letters/symbol needed only by pipelineDriverThread's own
|
||||||
|
"pipesrc | pipesink" command below -- same cross-checked-against-
|
||||||
|
scancodeLower sourcing as the block above (index 0x19 is 'p', 0x17 is
|
||||||
|
'i', 0x12 is 'e', 0x1F is 's', 0x13 is 'r', 0x31 is 'n', 0x25 is 'k'
|
||||||
|
(equal to consoleDemoScancodeK above, kept as this file's own named copy
|
||||||
|
for this demo, the same "each caller of this pattern owns its own named
|
||||||
|
copy" choice jobctl.c/kmain.c/syscall.c already make elsewhere), 0x2B is
|
||||||
|
'\\', shifted into '|' by scancodeUpper the same way scancodeMakeSeven
|
||||||
|
is shifted into '&' above). */
|
||||||
|
#define scancodeMakeP 0x19
|
||||||
|
#define scancodeMakeI 0x17
|
||||||
|
#define scancodeMakeE 0x12
|
||||||
}
|
}
|
||||||
|
|
||||||
void kmain(uint32 magic, uint64 multibootInfo)
|
void kmain(uint32 magic, uint64 multibootInfo)
|
||||||
|
|||||||
Reference in new issue
Block a user