m³OS docs GitHub

Phase 39

Unix Domain Sockets

The OS supports `AF_UNIX` (Unix domain) sockets for high-performance local inter-process communication. Programs can create stream and datagram sockets bound to filesystem paths. `socketpair()` creates connected pairs for parent-child IPC after `fork()`. This is the primary IPC mechanism on Linux, used by D-Bus, X11, systemd, PostgreSQL, Docker, and many other systems.

Complete

Learning goal

Understand why Unix domain sockets exist: local IPC without network overhead.

Category

infrastructure

Why this phase matters

The OS supports AF_UNIX (Unix domain) sockets for high-performance local inter-process communication. Programs can create stream and datagram sockets bound to filesystem paths. socketpair() creates connected pairs for parent-child IPC after fork(). This is the primary IPC mechanism on Linux, used by D-Bus, X11, systemd, PostgreSQL, Docker, and many other systems.

How it works in m3OS

The roadmap entry defines the learning scope for this phase; a deeper implementation doc has not been matched yet.

How it builds on earlier phases

This phase builds on Filesystem Enhancements and turns that foundation into Unix Domain Sockets.

Implementation outline

  • Add UnixSocket struct and UNIX_SOCKET_TABLE in kernel/src/net/unix.rs.
  • Add FdBackend::UnixSocket variant to process FD infrastructure.
  • Extend sys_socket() to accept AF_UNIX and allocate Unix sockets.
  • Implement sys_socketpair() with real Unix socket pairs (replace pipe delegation).
  • Implement bind() for named sockets (create socket file in VFS).
  • Implement connect() to named sockets (find socket file, establish connection).
  • Implement listen() + accept() for stream sockets.
  • Implement read()/write() for connected stream sockets.
  • Implement sendto()/recvfrom() for datagram sockets.
  • Add poll/epoll support for Unix socket fds.
  • Add shutdown() support for Unix sockets.
  • Test: socketpair() for parent-child communication.
  • Test: named stream socket server/client.
  • Test: datagram socket message boundary preservation.

Task progress snapshot

131 of 131 tracked tasks are complete.

Documentation targets

  • Documentation deliverables have not been listed yet.

Deferred follow-on work

  • SCM_RIGHTS (file descriptor passing)
  • SCM_CREDENTIALS (credential passing)
  • SOCK_SEQPACKET
  • Abstract namespace sockets
  • SO_PEERCRED
  • Autobind
  • sendmsg/recvmsg with ancillary data

Source materials

Important code

Code spotlight cards

Code spotlight

Define `UnixSocketType` and `UnixSocketState` enums

kernel/src/net/unix.rs L19-L27

and datagram sockets have fundamentally different behavior (connection-oriented. Why it matters: These enums drive all state-machine transitions. Stream. Focus on `UnixSocketType` and `UnixSocketState` in `kernel/src/net/unix.rs` while you trace the snippet.

// A.1 — UnixSocketType and UnixSocketState enums
// ===========================================================================

/// Unix socket type: stream (connection-oriented) or datagram (connectionless).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnixSocketType {
    Stream,
    Datagram,
}

Code spotlight

Add `FdBackend::UnixSocket` variant

kernel/src/process/mod.rs L91-L114

poll() based on the FdBackend variant. A new variant is needed so the syscall. Why it matters: The FD table dispatches read(), write(), close(), and. Focus on `FdBackend::UnixSocket` and `read` in `kernel/src/process/mod.rs` while you trace the snippet.

/// Backing store for an open file descriptor.
#[derive(Clone)]
pub enum FdBackend {
    /// FD 1/2 stdout/stderr — writes go to serial output.
    Stdout,
    /// FD 0 stdin — reads block until data is available from the kernel stdin buffer.
    Stdin,
    /// Read-only static ramdisk file (pointer + length into kernel .rodata).
    Ramdisk {
        content_addr: usize,
        content_len: usize,
    },
    /// Writable tmpfs file, identified by its path (e.g. "foo/bar.txt"
    /// relative to tmpfs root — no leading `/tmp/`).
    Tmpfs { path: String },
    /// FAT32 on-disk file (Phase 24). Stores the relative path within /data,
    /// the start cluster, file size, and parent directory cluster.
    Fat32Disk {
        path: String,
        start_cluster: u32,
        file_size: u32,
        dir_cluster: u32,
    },
    /// ext2 on-disk file (Phase 28). Stores the root-relative path

Code spotlight

Route `read()` and `write()` syscalls for Unix sockets

kernel/src/arch/x86_64/syscall.rs L35-L36

writes via standard read(fd, buf, len) and write(fd, buf, len). These must. Why it matters: Once a Unix stream socket is connected, userspace reads and. Focus on `read` and `write` in `kernel/src/arch/x86_64/syscall.rs` while you trace the snippet.

#[allow(dead_code)]
const NEG_EAGAIN: u64 = (-11_i64) as u64;

Key files

  • kernel/src/net/unix.rs — New struct in kernel/src/net/unix.rs managing the state machine for each Unix domain
  • kernel/src/net/mod.rs — The existing SOCKET_TABLE in kernel/src/net/mod.rs and FdBackend::Socket handle
  • kernel-core/src/pipe.rs — Uses ring buffer patterns from pipes (kernel-core/src/pipe.rs) for socket data buffers.

How real OSes differ

  • **SCM_RIGHTS** — Linux passes file descriptors between processes over Unix sockets.
  • **SCM_CREDENTIALS** — Linux passes sender's PID/UID/GID as ancillary data.
  • **SOCK_SEQPACKET** — Connection-oriented with message boundaries (combines stream reliability with datagram framing).
  • **Abstract namespace** — Linux-specific sockets without filesystem entries (`\0name`).
  • **Autobind** — Kernel assigns abstract addresses automatically.
  • **SO_PEERCRED** — Query connected peer's credentials via `getsockopt()`.
  • **sendmsg/recvmsg** — Scatter-gather I/O with ancillary data (control messages).

Success criteria

  • `socketpair(AF_UNIX, SOCK_STREAM, 0, sv)` creates a working connected pair.
  • A server can `bind` + `listen` + `accept` on a named socket path.
  • A client can `connect` to the named socket and exchange data bidirectionally.
  • `poll()` and `epoll` work with Unix domain socket fds.
  • Datagram sockets preserve message boundaries.
  • `unlink()` removes a named socket from the filesystem.
  • Socket file permissions are enforced (non-root cannot connect to root-owned socket).
  • `shutdown()` half-close works (SHUT_RD, SHUT_WR, SHUT_RDWR).
  • Non-blocking mode (`O_NONBLOCK`) works for Unix sockets.
  • All existing tests pass without regression.

Related phases

Keep exploring the roadmap