65 lines
3.1 KiB
Markdown
65 lines
3.1 KiB
Markdown
---
|
|
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`.
|