--- 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/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`.