SchOS/.claude/memory/schos-performance-and-init.md
Nexoniarz a1c965da1e include: one folder per subsystem, and includes that name it
Sixty-five headers sat side by side in include/, so `#include "irq.h"`
said nothing about where irq.h was or what it belonged to, and errno.h,
stat.h and fcntl.h at the top shadowed the system's for any host tool
built with -I./include. They are in folders now -- arch/ core/ fs/
dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and
every include names the folder, as Linux's do: "core/proc.h".

Done mechanically and checked the hard way: the kernel built from this
tree disassembles identically to the one built from the tree before
it. The full image boots, and userland is the same set of programs.
Paths in docs and comments follow; docs/guides/layout.md has a table
of what is where, and tools/fix-includes.py rewrites old-style
includes from the folders in the tree, for code written before this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
2026-10-23 15:05:12 +02:00

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