SchOS/.claude/memory/schunix-shm-and-mmap.md
Nexoniarz 3182da3965 Carry project memory in the repo, with tools/memory-sync.sh
Claude Code keeps per-project memory at <home>/.claude/projects/<key>/,
where <key> is the project's absolute path with every separator and the
drive colon turned into a dash. That makes it both rename-fragile and
machine-specific, and this project has hit each of those once:

  - D:\DEV\Schunix -> D:\DEV\SchOS changed the key, and 43 memories were
    invisible until moved across by hand.
  - Development happens from Windows and from Linux, and each looks in a
    different place for the same checkout.

Committing .claude/memory/ fixes both -- git carries it across renames
and between hosts -- and `import` puts it wherever the local machine
expects. .claude/settings.local.json stays ignored: those are per-machine
permissions.

The colon maps to a dash rather than being dropped. Deleting it gives
D-DEV-SchOS, which matches nothing and silently finds zero memories; that
was the first attempt. On Git Bash the key comes from `cygpath -w`,
because Claude Code is a Windows program and sees D:\DEV\SchOS, not
/d/DEV/SchOS.
2026-08-09 14:27:22 +02:00

48 lines
2.5 KiB
Markdown

---
name: schunix-shm-and-mmap
description: Shared memory and file-backed mmap work now; the rule is that pages a process does not own must never reach kfree
metadata:
node_type: memory
type: project
originSessionId: a665e14b-86bd-4b41-a228-51377f3e9225
modified: 2026-08-04T18:45:47.215Z
---
**Shared memory and file-backed mmap were implemented on 2026-08-04**, in
`kernel/sys/sys_mem.c`. These were the two syscall gaps blocking a display
server: without shm, every frame is a copy through a pipe.
- `shmget`/`shmat`/`shmdt` — up to 16 segments of 2 MB. Pages are allocated up
front and zeroed (a fresh segment handing out the last tenant's bytes would
leak them between processes). A segment lives until its last attachment
goes. **Their table.inc entries were `NO_LINUX`**, so a musl-linked program
never reached them at all; they now carry Linux's numbers (29/30/67).
- `mmap` of a regular file reads the contents in. It used to silently return
zeroed anonymous pages -- success-shaped, and wrong. **Writable `MAP_SHARED`
on a file returns `-ENODEV`** rather than faking it: its whole contract is
that stores reach the file, and there is no page cache that could make that
true.
**The rule that matters: pages a process does not own must never reach
kfree().** Three places had to learn it, and each was a real hazard:
- `vm_unmap_range()` kfree's everything it unmaps. Use
`vm_unmap_norelease()` for device memory and shared segments.
- `freevm()` frees any mapped page below `PHYSTOP`. The framebuffer survived
only by accident (0xfd000000 is above PHYSTOP); shm pages are kalloc'd and
sit below it. `proc_release_shared()` runs before freevm in both `exit()`
and `exec()` -- and in exec it must run **before** the `memset(proc->vma)`,
which erases the record of which mappings were shared.
- `fork()` must map the *same* physical pages and bump the attachment count.
`copy_vma_range()`'s share branch now copies the parent's PTEs rather than
special-casing the framebuffer address.
`struct vma`'s `is_fb` became `no_free` + `shmid` for this reason.
**Test with `shmtest`** (`cmd/shmtest`, nine checks) — it forks and has the
child write where the parent reads, which is the only convincing proof. Run
via `pwsh -File tools/bootcheck.ps1 -Tty`.
Still missing for a GUI: raw keyboard/mouse events that bypass the tty line
discipline, and unix domain sockets (`socket()` is still a NO_LINUX stub).
See [[schunix-console-tty]] and [[schunix-framebuffer-device]].