Skip to content

Services

miroslaw edited this page Sep 29, 2026 · 1 revision

Services

Three userspace services currently exist: console, exy-vfs, and echo. All follow the same pattern: receive an ATTACH on the bootstrap channel with a reply channel as a capability, then loop serving requests.

Console service

Source: src/userspace/console/main.c. Headers: src/userspace/include/exyde/console_rpc.h, console_client.h. Client lib: libc/console_client.c.

The console server wraps the kernel's KPUTS primitive. It exists so that when real device drivers arrive, the console can be repointed without touching every writer. For now it is a thin passthrough.

RPC ops:

CONSOLE_OP_ATTACH     client -> server: register ch_resp
CONSOLE_OP_WRITE      client -> server: bytes to print
CONSOLE_OP_READ       client -> server: read (returns 0 = EOF)
CONSOLE_OP_SHUTDOWN   ask the server to exit

Two channels: ch_req (client → server) and ch_resp (server → client). Same pattern as VFS.

The libc write(1/2, ...) path uses the console server when it is attached and falls back to SYS_KPUTS otherwise. read(0, ...) returns -EBADF without a live console.

Orderly shutdown matters: if the client closes ch_req while the server is blocked on IPC_RECV, the channel's destructor panics. init therefore calls console_client_shutdown() and waits for the child before exiting.

VFS service (exy-vfs)

Source: src/userspace/vfs/. Headers: src/userspace/include/exyde/vfs_rpc.h, vfs_client.h. Client lib: libc/vfs_client.c and libc/vfs_fdtab.c.

The server holds a RAMFS tree and a table of open files. Clients get a small integer fd back from OPEN; subsequent READ / WRITE / CLOSE / SEEK use it.

RPC ops:

VFS_OP_ATTACH     client -> server: register ch_resp
VFS_OP_OPEN       path, flags, mode  -> fd
VFS_OP_CLOSE      fd                 -> 0
VFS_OP_READ       fd, len            -> bytes
VFS_OP_WRITE      fd, bytes          -> bytes
VFS_OP_SEEK       fd, offset, whence -> new offset
VFS_OP_MKDIR      path, mode         -> 0
VFS_OP_UNLINK     path               -> 0
VFS_OP_RMDIR      path               -> 0
VFS_OP_READDIR    fd, index, name    -> 0 / -ENOENT
VFS_OP_SHUTDOWN   ()                 -> 0

The request and response structs are fixed-size (vfs_req, vfs_rsp) and fit in a single IPC_MSG_SIZE_MAX == 1024 message.

Client side

libc/vfs_client.c exposes:

vfs_client_init(ch_req, ch_resp)
vfs_client_open / close / read / write / seek
vfs_client_mkdir / unlink / rmdir / readdir
vfs_client_shutdown

Every public entry point checks that vfs_client_init has been called and returns -1 with ENOSYS otherwise. A program that never touches VFS gets a clean error instead of a crash.

POSIX layer

libc/vfs_fdtab.c keeps a per-process table mapping local fds (>= 3) to server-side fds. open, read, write, close, lseek in libc/unistd.c branch on the fd number:

  • fd in [0, 3) — stdio. Console RPC or SYS_KPUTS.
  • fd >= 3 — local table lookup + VFS RPC.
  • open() always goes through VFS.

Echo service

Source: src/userspace/echo/echo.c.

Minimal service: receives a fixed-size message, sends the same message back. Exits on the literal string quit. Used by init and by the SPAWN_ELF test.

Adding a new service

  1. Create src/userspace/<name>/main.c.
  2. In main, get the bootstrap handle with exyde_get_bootstrap(), receive ATTACH on it, extract the reply channel from the capability field, and enter your RPC loop.
  3. Add the binary to userspace/Makefile.
  4. Add it to tools/mkinitrd.sh's program list so it lands in the initrd as bin/<name>.
  5. In init.c, call spawn_from_initrd("bin/<name>", "<name>", ch) and wire up a client.

The kernel is not touched. Services are pure userspace.

Clone this wiki locally