memory: performance, init signal safety, and trusting the harness
This commit is contained in:
parent
686d5cf006
commit
8a4ac03ea9
3 changed files with 114 additions and 0 deletions
|
|
@ -1,6 +1,8 @@
|
|||
- [SchOS rename + git](schos-rename-and-git.md) — Schunix is SchOS and the kernel is SchIFT; the tree is under local git, and the two bugs the rename caused.
|
||||
- [Cross-platform build](schos-cross-platform-build.md) - Windows, Linux, macOS and BSD all build it; clang everywhere, and the four host assumptions that used to break.
|
||||
- [Memory sync](schos-memory-sync.md) - memory lives in the repo at .claude/memory/; tools/memory-sync.sh moves it between machines.
|
||||
- [Performance and init](schos-performance-and-init.md) - BSIZE must equal SCHFS_BLKSIZE (4-34x), and why Ctrl-C could kill pid 1.
|
||||
- [Trusting bootcheck](schos-bootcheck-trust.md) - it silently stopped running; count the [ok] lines, never just grep FAIL.
|
||||
- [Schunix build environment](schunix-build-environment.md) — builds natively on Windows with clang/lld + mingw + Git Bash; WSL is gone, and the five latent bugs the move exposed.
|
||||
- [Syscalls: one table](schunix-syscall-single-table.md) — declare in kernel/sys/table.inc only; linux_abi.c is generated, never hand-edited.
|
||||
- [Linux ABI decision](schunix-linux-abi-decision.md) — the native syscall ABI is being dropped; Linux numbering becomes the only ABI.
|
||||
|
|
|
|||
47
.claude/memory/schos-bootcheck-trust.md
Normal file
47
.claude/memory/schos-bootcheck-trust.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
name: schos-bootcheck-trust
|
||||
description: bootcheck silently stopped running and five of seven modes asserted nothing; how to tell a real pass from a script that died
|
||||
metadata:
|
||||
node_type: memory
|
||||
originSessionId: 9d8a2aef-bcf0-47a0-96c6-f706efab4a95
|
||||
modified: 2026-08-09T13:55:00.546Z
|
||||
---
|
||||
|
||||
**A green run is only green if you counted the checks.** On 2026-08-09 the
|
||||
bootcheck suite was reported passing seven times when it had not executed at
|
||||
all, and separately when five of its seven modes asserted nothing whatsoever.
|
||||
|
||||
**Why:** `bootcheck.ps1` had `[string]$Qemu = (Find-Qemu)` as a param default.
|
||||
PowerShell binds parameter defaults *before* any function in the script body is
|
||||
defined, so it died on its first line with "Find-Qemu is not recognized" —
|
||||
text containing neither `FAIL` nor `Exception`, which were the two words being
|
||||
grepped for. Empty output was read as success. Resolve after the param block:
|
||||
`if (-not $Qemu) { $Qemu = Find-Qemu }`.
|
||||
|
||||
**And:** an earlier index-based edit (`s[:a] + inner + s[c:]`) that removed the
|
||||
obsolete `-SchFS` block also deleted the `Expect` blocks for `-Tty`, `-Usb`,
|
||||
`-Sock`, `-Ps` and `-Evdev`. Those modes still booted, still printed, and still
|
||||
exited 0 — asserting nothing. It happened before `git init`, so no commit ever
|
||||
had them; they were reconstructed from failure text quoted earlier in the
|
||||
transcript.
|
||||
|
||||
**How to check a run is real:**
|
||||
|
||||
```sh
|
||||
grep -c '\[ok\]' # must be non-zero, and roughly what you expect
|
||||
grep -c 'boot check passed'
|
||||
```
|
||||
|
||||
Counting `FAIL` alone cannot distinguish "passed" from "never ran". Expected
|
||||
counts as of 2026-08-09: base 23, Tty 47, Ps 28, Sock 60, Fb 32, Usb 27,
|
||||
Evdev 25 — Tty and Sock include `[ok]` lines printed by the guest programs
|
||||
themselves.
|
||||
|
||||
**Never pipe a long bootcheck through `head`.** It closes the pipe, kills the
|
||||
script partway, and looks like a hang. Redirect to a file and grep the file.
|
||||
|
||||
**Why:** the general lesson is that absence of failure output is not evidence
|
||||
of success. Both of these produced clean-looking runs while testing nothing.
|
||||
|
||||
**How to apply:** after any change to the harness itself, verify the check
|
||||
count, not just the absence of FAIL.
|
||||
65
.claude/memory/schos-performance-and-init.md
Normal file
65
.claude/memory/schos-performance-and-init.md
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
---
|
||||
name: schos-performance-and-init
|
||||
description: BSIZE must equal SCHFS_BLKSIZE or everything is slow; init must ignore default-action signals or Ctrl-C kills the machine
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: 9d8a2aef-bcf0-47a0-96c6-f706efab4a95
|
||||
modified: 2026-08-09T13:54:32.858Z
|
||||
---
|
||||
|
||||
## BSIZE must equal SCHFS_BLKSIZE
|
||||
|
||||
`BSIZE` was 1024 (xv6's) while a SchFS block is 4096, so **every SchFS block
|
||||
cost four `bread()` calls**, and each `bread()` is one IDE request with its own
|
||||
interrupt and sleep/wakeup. `exec()` of the 800 KB toybox binary meant ~800
|
||||
round trips — about two seconds, on **every** command.
|
||||
|
||||
The measurement that found it: `seq 1 200`, which touches no file and prints
|
||||
nothing, cost the same 2.0s as `cat /bin/toybox`; and `ls | wc -l` cost exactly
|
||||
double. Both are dominated by loading the same binary. **When everything is
|
||||
slow by the same constant, measure something that does nothing** — the floor is
|
||||
the bug.
|
||||
|
||||
seq 1 200 2.02s -> 0.06s
|
||||
ls /bin 2.76s -> 0.68s
|
||||
ls /bin | wc -l 4.68s -> 0.73s
|
||||
|
||||
Three coupled changes, all required together:
|
||||
|
||||
1. `BSIZE` 1024 → 4096 (`include/fs.h`). Keep it equal to `SCHFS_BLKSIZE`.
|
||||
2. `NBUF` 90 → 256 blocks (1 MB). At 90 KB the cache was **smaller than the
|
||||
binary every command execs**, so nothing was ever resident on the next
|
||||
command. This is what makes the *second* command fast.
|
||||
3. `ideinit()` issues **SET MULTIPLE MODE** (0xC6). READ/WRITE MULTIPLE move
|
||||
the drive's *multiple count* of sectors per interrupt, and that count comes
|
||||
from this command — **not** from the sector-count register. Nothing had ever
|
||||
issued it, which is the real reason `idestart()` carried
|
||||
`if (sector_per_block > 7) panic("idestart")`: BSIZE was two sectors and
|
||||
nobody had tested further. Without it, a 4096-byte transfer desynchronises
|
||||
and reads as filesystem corruption.
|
||||
|
||||
Still open: the *first* command after boot pays a cold ~2.4s. Per-request cost
|
||||
under QEMU PIO dominates; DMA would be the next step.
|
||||
|
||||
`FSSIZE` is in blocks, so at BSIZE 4096 a full v1 image is ~600 MB. That is why
|
||||
the USB test gets its own 16 MB `build/usbstick.img` (`make usbstick`) rather
|
||||
than `build/fs.img`.
|
||||
|
||||
## init must ignore default-action signals
|
||||
|
||||
**Ctrl-C could kill pid 1 and panic the machine.** SIGINT goes to the
|
||||
foreground process group, init is pgid 1, and any window where `tty_fg_pgrp` is
|
||||
1 — before a shell claims the terminal, after one exits, or between the two
|
||||
while the key is held down — lands SIGINT on init. No handler, so `killed` was
|
||||
set, `exit()` ran, and `exit()` panics outright on `initproc`. Spamming Ctrl-C
|
||||
at a frozen program is what finds that window.
|
||||
|
||||
`init_ignores()` in `kernel/proc/proc.c` drops a signal for init when
|
||||
`sig_handler[sig] == 0`, in **both** `sigsend()` and `sigsend_pgrp()`. SIGKILL
|
||||
included — init is not killable from userspace on any Unix, and a guard that
|
||||
covers SIGINT but not SIGKILL is half a fix. A handler init did install is
|
||||
still delivered.
|
||||
|
||||
Regression test lives in the base bootcheck run: `kill -INT 1`, `-KILL 1`,
|
||||
`-TERM 1`, then `INIT-SURVIVED`.
|
||||
Loading…
Reference in a new issue