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
|
* rather than silently falling back to the "no timestamp" dash this same
|
||||||
* code path prints for tmpfs/dev entries.
|
* 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)
|
void kmain(uint32 magic, uint64 multibootInfo)
|
||||||
@@ -1756,6 +1876,12 @@ void kmain(uint32 magic, uint64 multibootInfo)
|
|||||||
runHeapTest();
|
runHeapTest();
|
||||||
runPagingTest();
|
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);
|
pitInit(timerFrequency);
|
||||||
kprintf("[pit] timer at %u Hz\n", 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");
|
kprintf("[vfs] no multiboot2 module present\n");
|
||||||
vfsMountTmpfs("/tmp");
|
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("shell", shellThread);
|
||||||
threadCreate("workerA", workerA);
|
uint32 workerAId = threadCreate("workerA", workerA);
|
||||||
threadCreate("workerB", workerB);
|
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");
|
kprintf("\n[boot] launching userland programs from the ramdisk into ring 3\n");
|
||||||
spawnUser("/bin/hello", "hello", 0x48000000, 0);
|
spawnUser("/bin/hello", "hello", 0x48000000, 0);
|
||||||
|
|||||||
Reference in new issue
Block a user