docs: architecture describes what is built now
kernel (threads, demand paging, TLB shootdowns, dynamic linking, RAM direct map), syscalls, filesystem (/proc, /sys, /dev), terminals (12, VT handshake, ptys), networking (r8169 through LDLS), LDLS (module sets; Linux code may ship), devices (what stays in the kernel; hybrid.md's lasting content), boot (the loader's placement of the kernel), graphics (fbdev, GMMOS, QR 1-40), audio (ALSA over OSS), debugging, usb. New: packages.md (the apk root) and desktop.md (Xorg on the framebuffer). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EVRhxi9zpdW2uDUN4FVcao
This commit is contained in:
parent
655c7d8d01
commit
2556a955b3
15 changed files with 921 additions and 341 deletions
102
docs/architecture/packages.md
Normal file
102
docs/architecture/packages.md
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# Packages: an apk root
|
||||
|
||||
Alpine is musl with a different init, and SchOS is musl with a different kernel.
|
||||
Alpine's binaries run on SchOS unmodified — its dynamic linker, its busybox, and
|
||||
`apk` itself, fetching over TLS through SchOS's network stack. So instead of
|
||||
porting programs one at a time, SchOS installs them.
|
||||
|
||||
## The model
|
||||
|
||||
The root filesystem is an apk-managed root:
|
||||
|
||||
| Part | Where it comes from |
|
||||
|---|---|
|
||||
| musl, busybox, apk-tools, alpine-keys, CA certificates | Alpine's minirootfs (`ports/alpine`) |
|
||||
| what `ports/alpine/world` lists (zsh, Xorg, Window Maker, htop, NetSurf …) | Alpine's packages, pinned in `world.lock` |
|
||||
| schinit, its jobs, SchOS's tools, toybox, mksh, configuration | **schos-base**, an apk package the build makes from `sysroot/` |
|
||||
|
||||
`apk add` installs anything from Alpine's main and community repositories.
|
||||
`apk upgrade` updates Alpine's packages, and schos-base from the SchOS repository
|
||||
at `/var/lib/schos/repo`.
|
||||
|
||||
PID 1 is still schinit. Alpine's OpenRC is not installed, and packages that ship
|
||||
OpenRC services install them where nothing reads them.
|
||||
|
||||
## Who owns what
|
||||
|
||||
Where Alpine and SchOS both have a program, Alpine's wins, and SchOS keeps only
|
||||
what is its own (`tools/posix/mkaroot.sh` says so in its header):
|
||||
|
||||
- `/bin/sh`, `cat`, `sed`, `find` and the rest are busybox. Alpine's package scripts
|
||||
are written against busybox.
|
||||
- zsh is Alpine's. SchOS keeps its `zshrc` and `zshenv` (`replaces=zsh`).
|
||||
- `/etc/os-release` and `/etc/motd` are SchOS's (`replaces=`).
|
||||
- `passwd`, `group` and `profile` are Alpine's. schos-base's post-install sets root's
|
||||
shell to zsh, and SchOS's profile is `/etc/profile.d/schos.sh`.
|
||||
- `pci.ids` and `usb.ids` are Alpine's (`hwdata-pci`, `hwdata-usb`): Xorg depends on
|
||||
`hwdata-pci`, and two owners of one file is an error apk will not install past.
|
||||
- mksh is `/bin/mksh`. schinit's recovery shell looks for it first because it is static:
|
||||
a broken musl cannot take recovery down with it.
|
||||
|
||||
## Building
|
||||
|
||||
make qemu boots build/schfs.img, the root as a 1 GB SchFS disk
|
||||
make iso the same root as the live system's compressed ramdisk
|
||||
make alpine-lock re-resolve world against Alpine (needs the network)
|
||||
|
||||
It is the only root; `sysroot/` is what schos-base is packaged from. Only
|
||||
`alpine-lock` asks Alpine what is current. A normal build installs exactly what
|
||||
`world.lock` names — files in `dist/alpine/`, fetched once and checked against the
|
||||
lock's sha256, and against Alpine's signatures by apk — and never asks what is newer.
|
||||
`world.lock` resolves the handful of names in `world` into a few hundred files, every
|
||||
dependency included, so every image after the first is reproducible.
|
||||
|
||||
schos-base and its repository index are signed with a key made on the first build
|
||||
(`build/apk-keys/`, never committed). The root trusts it beside Alpine's keys. The
|
||||
install runs as an ordinary user with `--no-scripts`, because apk's scripts want a
|
||||
chroot; `mkaroot.sh` does by hand the one change schos-base's post-install would
|
||||
have made.
|
||||
|
||||
## What it took from the kernel
|
||||
|
||||
All of it found by running apk, and none of it specific to apk:
|
||||
|
||||
- **TCP receive window:** it reopened before the reader drained, so lwIP refused data
|
||||
and the sender waited on retransmission timers (5 KB/s). It reopens on read now, with a
|
||||
32-segment window; downloads run at tens of megabits.
|
||||
- **`mmap` address reuse:** freed ranges were never reused, so musl's allocator used up the
|
||||
256 mapping records with 780 MB of RAM free.
|
||||
- **`unlinkat` / `renameat`** returned a bare `-1`, read as `EPERM`, where apk expects
|
||||
`ENOENT`; `renameat` could not replace its target.
|
||||
- **`#!` scripts in `execve`**, and `ENOEXEC`: apk runs triggers directly.
|
||||
- **`chroot`**, and mounting devtmpfs and proc more than once.
|
||||
- Later, as bigger programs arrived: dynamic linking, threads and `futex`, demand paging,
|
||||
pseudo-terminals, `inotify`, `flock`, Unix-socket credentials. Each is in
|
||||
[kernel.md](kernel.md) or [syscalls.md](syscalls.md) because a package asked for it.
|
||||
|
||||
## The live system
|
||||
|
||||
The live ISO runs the apk root from RAM. The ramdisk is the compressed image
|
||||
([scff.md](scff.md)); at boot the volume grows to half of the free memory (4 GB of root on
|
||||
an 8 GB machine), backed by pages as they are written. Xorg and its dependencies are in the
|
||||
image already; anything else installs into that space in about a minute, over the network,
|
||||
and **is gone at reboot**.
|
||||
|
||||
## What it does not do
|
||||
|
||||
- **No installer.** Nothing is written to the machine's disks, so packages do not survive
|
||||
a reboot. Keeping them needs an installer that writes the root to a SATA or NVMe disk,
|
||||
and a kernel that finds its root there by the SchFS GPT type rather than as IDE disk 1.
|
||||
- **Not every Alpine package runs.** The ones in `world` were chosen *because* they run:
|
||||
terminal programs that draw and poll (htop, btop, tmux, mc), X clients, SDL 2 games,
|
||||
NetSurf on GTK 3. Others hit whatever the kernel does not do yet; Epiphany (WebKit) is
|
||||
one — its web-process child dies at start-up, and it has not been run to ground.
|
||||
A package is one `apk add` away, and finding what it needs is how most of the kernel's
|
||||
Linux surface got written.
|
||||
- **Fortified libc is strict, and Alpine's packages are not always correct.** `lbreakout2`
|
||||
hands `vsnprintf` a size larger than its buffer and traps on Alpine too; `world` carries
|
||||
`lbreakouthd` instead.
|
||||
- **apk's scripts and triggers do not run at build time** (`--no-scripts`), so a package
|
||||
whose script matters has to have it done by hand in `mkaroot.sh`.
|
||||
- **OpenRC services do nothing.** Anything that expects an init script to start it has to
|
||||
be given a schinit job ([init.md](init.md)).
|
||||
Loading…
Reference in a new issue