feat: extend kmain with lsDriverThread and andOrTypeFailChainCommand
This commit is contained in:
1 parent
68907b39ec
commit
21f2719413
1 file changed
+180
-2
+180
-2
@@ -1715,6 +1715,126 @@ static void lsTypeLongListCommand(void)
|
||||
* rather than silently falling back to the "no timestamp" dash this same
|
||||
* code path prints for tmpfs/dev entries.
|
||||
*/
|
||||
static void lsDriverThread(void)
|
||||
{
|
||||
threadSleep(lsDriverInitialDelayMs);
|
||||
while (currentThread()->state == threadSleeping)
|
||||
halt();
|
||||
|
||||
kprintf("[kbd] typing shell commands for the ls -l demo\n");
|
||||
jobctlTypeFlushLine();
|
||||
|
||||
lsTypeLongListCommand();
|
||||
threadSleep(lsDriverStepSettleMs);
|
||||
while (currentThread()->state == threadSleeping)
|
||||
halt();
|
||||
|
||||
for (;;)
|
||||
halt();
|
||||
}
|
||||
|
||||
/* 20500ms clears lsDriverThread's own total budget (19500 initial + 300
|
||||
settle = 19800ms) with 700ms margin -- smaller than most prior driver
|
||||
threads' own margin since `ls -l` is a plain builtin (listDir, shell.c)
|
||||
with no process spawn/VFS-blocking wait after its own settle delay,
|
||||
unlike e.g. globDriverThread's globcat.c spawn+read+compare, so there is
|
||||
no extra real execution time to wait out beyond the typing itself. Same
|
||||
"next demo's initial delay clears the previous demo's total budget with
|
||||
margin" convention every driver thread above establishes. Only ONE
|
||||
command, ONE settle step -- false.c is the smallest possible spawned
|
||||
program in this kernel (immediate userExit, no VFS/pipe/signal work at
|
||||
all), and this fala's own live demo needs only one line of it (see
|
||||
andOrTypeFailChainCommand's own comment for why one line already proves
|
||||
the whole feature), the same budget-consciousness
|
||||
lsDriverInitialDelayMs's own comment established: an EARLIER version of
|
||||
this demo also typed a second line ("true && echo aoy", proving `&&`
|
||||
fires on a real SUCCESS too) and was cut after it measurably pushed one
|
||||
real re-run over tools/ci-test.sh's fixed 25-real-second QEMU ceiling
|
||||
under this session's own elevated host load -- not worth the extra
|
||||
margin lost for a case the KTEST-level shellSplitAndOr tests (which
|
||||
assert both shellAndOrAnd and shellAndOrOr are parsed and recorded
|
||||
correctly) and this project's dozens of pre-existing successful
|
||||
`cmd && cmd`-free typed commands already make low-risk. */
|
||||
#define andOrDriverInitialDelayMs 20500
|
||||
#define andOrDriverStepSettleMs 300
|
||||
|
||||
/* "false && echo X || echo ao2" -- the classic shell if-then-else idiom in
|
||||
one line. false.c genuinely spawns and exits 1 (see its own file
|
||||
comment); `&&` sees that real nonzero status and SKIPS "echo X" (a
|
||||
builtin, would otherwise print instantly); the `||` check right after
|
||||
it is then evaluated against the SAME status "echo X" never changed
|
||||
(shellSplitAndOr/execute()'s own "a skipped segment leaves status
|
||||
completely unchanged" contract, shell.h) -- still nonzero, so it DOES
|
||||
run "echo ao2". The needle `ao2` can only ever appear if both the skip
|
||||
and the carry-through past that skip worked correctly; "X" is
|
||||
deliberately never checked for (this kernel's tools/ci-test.sh only
|
||||
ever asserts PRESENCE of a needle, never absence), so this line's own
|
||||
affirmative proof is entirely in `ao2` actually appearing at all. Every
|
||||
character this line needs (f/a/l/s/e/space/&/h/c/o/x/|/a/o/2) is already
|
||||
defined by an earlier demo's own scancode block in this file. */
|
||||
static void andOrTypeFailChainCommand(void)
|
||||
{
|
||||
injectKeyPress(scancodeMakeF);
|
||||
injectKeyPress(scancodeMakeA);
|
||||
injectKeyPress(scancodeMakeL);
|
||||
injectKeyPress(scancodeMakeS);
|
||||
injectKeyPress(scancodeMakeE);
|
||||
injectKeyPress(scancodeMakeSpace);
|
||||
injectShiftedKeyPress(scancodeMakeSeven);
|
||||
injectShiftedKeyPress(scancodeMakeSeven);
|
||||
injectKeyPress(scancodeMakeSpace);
|
||||
injectKeyPress(scancodeMakeE);
|
||||
injectKeyPress(scancodeCPressMake);
|
||||
injectKeyPress(scancodeMakeH);
|
||||
injectKeyPress(scancodeMakeO);
|
||||
injectKeyPress(scancodeMakeSpace);
|
||||
injectKeyPress(scancodeMakeX);
|
||||
injectKeyPress(scancodeMakeSpace);
|
||||
injectShiftedKeyPress(scancodeMakeBackslash);
|
||||
injectShiftedKeyPress(scancodeMakeBackslash);
|
||||
injectKeyPress(scancodeMakeSpace);
|
||||
injectKeyPress(scancodeMakeE);
|
||||
injectKeyPress(scancodeCPressMake);
|
||||
injectKeyPress(scancodeMakeH);
|
||||
injectKeyPress(scancodeMakeO);
|
||||
injectKeyPress(scancodeMakeSpace);
|
||||
injectKeyPress(scancodeMakeA);
|
||||
injectKeyPress(scancodeMakeO);
|
||||
injectKeyPress(scancodeMakeTwo);
|
||||
injectKeyPress(scancodeMakeEnter);
|
||||
}
|
||||
|
||||
/*
|
||||
* Live ring-3 proof for shell.c's new `cmd1 && cmd2 || cmd3 ...`
|
||||
* conditional chaining (shellSplitAndOr, executeSingle/execute) -- types
|
||||
* the command above at the real interactive shell, the same
|
||||
* injected-keystroke mechanism every shell-driven demo above already
|
||||
* uses. Unlike every earlier shell-syntax demo (pipelines, redirects,
|
||||
* here-strings, globs), the actual subject under test here is false.c --
|
||||
* a brand-new, deliberately trivial user program whose entire job is to
|
||||
* hand back a real, predictable, nonzero exit code from a genuine ring-3
|
||||
* spawn (see its own file comment) -- proving the new chaining logic
|
||||
* reacts to a REAL process's own exit status, not just a builtin's
|
||||
* in-kernel int return (which listDir/catFile/makeDir/changeDir/
|
||||
* doExport/doUnset already exercise on every OTHER typed command in this
|
||||
* whole boot sequence, just never through `&&`/`||` before this fala).
|
||||
*/
|
||||
static void andOrDriverThread(void)
|
||||
{
|
||||
threadSleep(andOrDriverInitialDelayMs);
|
||||
while (currentThread()->state == threadSleeping)
|
||||
halt();
|
||||
|
||||
kprintf("[kbd] typing shell commands for the &&/|| chaining demo\n");
|
||||
jobctlTypeFlushLine();
|
||||
|
||||
andOrTypeFailChainCommand();
|
||||
threadSleep(andOrDriverStepSettleMs);
|
||||
while (currentThread()->state == threadSleeping)
|
||||
halt();
|
||||
|
||||
for (;;)
|
||||
halt();
|
||||
}
|
||||
|
||||
void kmain(uint32 magic, uint64 multibootInfo)
|
||||
@@ -1756,6 +1876,12 @@ void kmain(uint32 magic, uint64 multibootInfo)
|
||||
runHeapTest();
|
||||
runPagingTest();
|
||||
|
||||
sigInstallTrampoline();
|
||||
kprintf("[sig] shared signal trampoline mapped at %p\n", (void*)sigTrampolineVirtualAddr);
|
||||
|
||||
sigInstallInfoPage();
|
||||
kprintf("[sig] shared siginfo page(s) mapped at %p\n", (void*)sigInfoVirtualAddr);
|
||||
|
||||
pitInit(timerFrequency);
|
||||
kprintf("[pit] timer at %u Hz\n", timerFrequency);
|
||||
|
||||
@@ -1773,9 +1899,61 @@ void kmain(uint32 magic, uint64 multibootInfo)
|
||||
kprintf("[vfs] no multiboot2 module present\n");
|
||||
vfsMountTmpfs("/tmp");
|
||||
|
||||
/* namedreader/namedwriter (spawned below, after userland launch) are two
|
||||
INDEPENDENT boot programs with no parent-child relationship and no
|
||||
shared argv -- unlike fifomaster.c's children, which get a pipe.h
|
||||
handle number handed to them, these two only agree on this path. The
|
||||
fifo has to exist before either one can open it. */
|
||||
if (vfsMkfifo("/tmp/rendezvous") == 0)
|
||||
kprintf("[vfs] mkfifo /tmp/rendezvous ready\n");
|
||||
else
|
||||
kprintf("[vfs] mkfifo /tmp/rendezvous FAILED\n");
|
||||
|
||||
if (ataInit())
|
||||
kprintf("[ata] primary master '%s', %lu sectors (%lu MiB), /dev/disk0 ready\n",
|
||||
ataModelString(), ataSectorCount(),
|
||||
(ataSectorCount() * ataSectorSize) / (1024 * 1024));
|
||||
else
|
||||
kprintf("[ata] no primary master drive detected, /dev/disk0 will read as empty\n");
|
||||
|
||||
if (sfsMount())
|
||||
kprintf("[sfs] mounted persistent filesystem at /mnt (%lu KiB usable)\n",
|
||||
sfsCapacityBytes() / 1024);
|
||||
else
|
||||
kprintf("[sfs] no usable disk for /mnt (drive missing or too small)\n");
|
||||
|
||||
ktestRunAll();
|
||||
|
||||
threadCreate("shell", shellThread);
|
||||
threadCreate("workerA", workerA);
|
||||
threadCreate("workerB", workerB);
|
||||
uint32 workerAId = threadCreate("workerA", workerA);
|
||||
uint32 workerBId = threadCreate("workerB", workerB);
|
||||
|
||||
/* Both workers are pure CPU hogs (busy loops, never sleep or block), so
|
||||
they are the cleanest possible demonstration that the CFS-lite
|
||||
scheduler (sched.c/prioq.h) actually enforces weight, not just that it
|
||||
compiles: workerA is given 3x workerB's weight, so it should accrue
|
||||
vruntime three times slower and therefore win the ready-queue's
|
||||
min-vruntime pick roughly three times as often. Compare the two
|
||||
threads' printed iteration counts at similar tick values to see it. */
|
||||
threadSetPriority(workerAId, schedDefaultWeight * schedPriorityWeightRatio);
|
||||
kprintf("[sched] workerA pid=%u weight=%u, workerB pid=%u weight=%u "
|
||||
"(CFS-lite: higher weight runs more often)\n",
|
||||
workerAId, schedDefaultWeight * schedPriorityWeightRatio, workerBId, schedDefaultWeight);
|
||||
threadCreate("schedVerify", schedPriorityVerifierThread);
|
||||
|
||||
pipeDemoHandle = pipeCreate();
|
||||
if (pipeDemoHandle >= 0) {
|
||||
threadCreate("pipeProducer", pipeProducerThread);
|
||||
threadCreate("pipeConsumer", pipeConsumerThread);
|
||||
} else {
|
||||
kprintf("[pipe] demo pipe creation failed\n");
|
||||
}
|
||||
|
||||
joinDemoWorkerId = threadCreate("joinWorker", shortLivedWorkerThread);
|
||||
if (joinDemoWorkerId != threadInvalidId)
|
||||
threadCreate("joiner", joinerThread);
|
||||
else
|
||||
kprintf("[sched] join demo worker not started: thread table full\n");
|
||||
|
||||
kprintf("\n[boot] launching userland programs from the ramdisk into ring 3\n");
|
||||
spawnUser("/bin/hello", "hello", 0x48000000, 0);
|
||||
|
||||
Reference in new issue
Block a user