AnotherOS

Documentation

The syscall ABI

A Linux-faithful system call surface — which is what lets unmodified musl, BusyBox and Go binaries run.

The kernel implements a Linux system call ABI. The surface is deliberately Linux-faithful, because that is what lets unmodified musl, BusyBox, GTK, Qt and Go binaries run at all: libraries call syscalls by number, so the numbers are not ours to choose.

This is the single most consequential design decision in the system. Everything that "just runs" here runs because of it.

Three number ranges

Standard Linux numbers

read, write, open, fork, execve, mmap, clone, futex, statx and the rest of the upstream table keep their upstream values. They are not negotiable and not ours.

One convention is worth knowing: where a call is implemented but the kernel does not yet maintain the underlying accounting, it returns zeroed data rather than an error. The list of calls in that state shrinks as the accounting lands — resource usage now reports real user and system times, for instance, while other fields in the same structure are still honest zeroes.

The reserved band

A range of numbers is reserved for their true Linux meanings, and nothing else may take them.

The AnotherOS-private range, at 1000 and above

Parked well clear of the Linux table, so adding a Linux syscall later can never collide.

This range exists because of a real bug. Private calls used to sit directly on top of real Linux numbers. That forced the dispatcher to guess intent from argument values, and it silently broke every libc routine that used those numbers. Moving them out is the fix, and putting a new private call anywhere else re-creates the problem.

Number Call
1000 readdir
1006–1023 ext2/3/4 direct-write helpers
1008 openpty
1013 httpget
1014 dnsresolve
1030–1034 Wi-Fi scan, results, connect, disconnect, status
1035 Network configuration
1040–1044 Framebuffer present, flip, vblank wait, flip setup, set mode
1045 Keyboard repeat

Timer and signal file descriptors — signalfd, timerfd, eventfd, memfd_create — use their real Linux numbers on purpose, so they fold into poll, ppoll and epoll exactly the way userland expects.

Process and memory

fork(2) is copy-on-write. Anonymous mmap is demand-paged. System V shared memory works, and swap can be activated and deactivated at runtime.

madvise(MADV_DONTNEED) is a real purge over private anonymous mappings: frames are dropped, copy-on-write safely, and the next touch reads zeroes. That is the Linux contract allocators rely on, and an implementation that accepted the call and did nothing would corrupt them quietly.

MADV_WIPEONFORK and MADV_DONTFORK are real too. In a fork child, a wipe-on-fork range reads as fresh zeroes and a don't-fork range is not mapped at all, while the parent keeps its bytes. These used to return success and do nothing — and fork-safe random number generators trust that return value, which is a security bug rather than a missing feature.

Readiness

poll() and select() must agree. A descriptor one of them calls ready and the other does not is a hang that only appears under load.

Polling is deadline-driven: a blocked poll(2) costs zero CPU, pollers are not woken by every timer tick, and every producer of readiness wakes its own pollers.

File notification

inotify is a real watch mechanism, not a descriptor that never fires. Events come off the filesystem mutation path: create, modify, delete, and rename as a matched pair sharing a cookie.

This matters because the alternative — a descriptor that opens successfully and stays silent — makes a file manager or a toolkit appear to work and then never notice a change, which is far harder to diagnose than an unimplemented call.

Signals

Fault signals carry a correct faulting address, so a handler can read it, read and modify the interrupted program counter, step past the fault and resume. That is the move JITs, garbage collectors and Windows' structured exception handling all depend on, and it is what makes the Win32 subsystem possible.

/proc

/proc is served from the real kernel state, with per-process directories that tools actually enumerate. A /proc that answers reads but lists nothing makes ps empty, which is exactly the moment you need it.

Robustness discipline

Every call validates its arguments before touching them, and a bad pointer from userland returns an error rather than faulting the kernel. Where behaviour is ambiguous, the rule is: do what Linux does, because the programs being run were written against what Linux does.