IPC Explained

In a monolithic kernel, the filesystem code calls the disk driver directly — they are in the same address space. In a microkernel, the filesystem and disk driver are separate programs. They need a way to talk to each other. That mechanism is IPC (Inter-Process Communication).

Why IPC Matters

In SaltyOS, every operation that crosses a server boundary is an IPC message:

  • Reading a file: your program → VFS server → block driver → hardware.

  • Creating a process: your program → process manager.

  • Allocating memory: your program → memory manager.

IPC is on the critical path of everything. A slow IPC mechanism makes the entire OS slow. This is why kernite has an assembly-optimized fastpath for the most common IPC patterns.

Two IPC Primitives

SaltyOS has two communication primitives, each designed for a specific pattern:

Endpoints (Synchronous)

An endpoint is like a phone call. Both parties must be present: the caller waits for the receiver to pick up, and the receiver waits for someone to call.

Client                 Endpoint                 Server
  |                      |                        |
  |--- Send(msg) ------->|                        |
  |    (client blocks)   |                        |
  |                      |<--- Recv() ------------|
  |                      |     (server was waiting)|
  |                      |--- deliver msg -------->|
  |                      |                        |
  |                      |<--- Reply(response) ---|
  |<-- response ---------|                        |
  |    (client unblocks) |                        |

Key properties:

  • Synchronous: the sender blocks until the receiver is ready (and vice versa).

  • Rendezvous: no buffering. Messages are transferred directly from sender to receiver.

  • Typed: messages carry a label (what operation), message registers (data), and optionally capabilities.

Notifications (Asynchronous)

A notification is like a flag or doorbell. You can ring it without waiting, and the listener checks it when ready.

Driver                 Notification              Server
  |                      |                        |
  |--- Signal(bits) ---->|                        |
  |    (never blocks)    | [bits stored]          |
  |                      |                        |
  |                      |<--- Wait() ------------|
  |                      |--- deliver bits ------->|
  |                      |                        |

Key properties:

  • Asynchronous: signaling never blocks. Bits accumulate via bitwise OR.

  • Lightweight: just a 64-bit word, no message body.

  • Used for interrupts: hardware IRQs are delivered as notification signals.

The Call/ReplyRecv Pattern

The most common IPC pattern is client-server RPC. SaltyOS optimizes it with two specialized syscalls:

Client Side: Call

Call = send a request + wait for the reply, in one syscall.

// Client: "read 4096 bytes from file"
msg.label = VFS_READ;
msg.regs[0] = fd;
msg.regs[1] = 4096;
result = Call(vfs_endpoint, msg);
// blocks until server replies
// result.regs[0..] contains the response

Server Side: ReplyRecv

ReplyRecv = reply to the previous client + wait for the next request, in one syscall.

// Server loop
loop {
    (badge, request) = ReplyRecv(my_endpoint, response);
    // badge identifies which client sent this request
    response = handle(badge, request);
}

This is efficient because the reply and the next receive happen atomically — no window where the server is idle.

What Happens Inside the Kernel

When a client calls Call and a server calls ReplyRecv:

  1. The client’s message is copied directly into the server’s registers (no intermediate buffer).

  2. The kernel switches from the client thread to the server thread.

  3. The server processes the request and calls ReplyRecv.

  4. The reply is copied back to the client’s registers.

  5. The kernel switches back to the client thread.

In the best case (short message, no capability transfer), this is handled by the IPC fastpath — an assembly-optimized path that skips most of the kernel’s generic dispatch logic.

Messages

An IPC message contains:

Field Description

Label

A 40-bit operation code. The server uses this to decide what to do (like a function name).

Length

How many message registers are used (0-127).

Message registers

Up to 127 64-bit values. MR0-MR3 are passed in CPU registers (fast). MR4+ overflow into the IPC buffer (a shared memory page).

Extra caps

Up to 4 capabilities can be transferred alongside the message.

Badge

A 64-bit value set by mint that identifies the sender to the receiver.

Notifications in Detail

Notifications are simpler than endpoints but serve a different purpose.

Signaling

Signal(notification, bits) performs an atomic OR on the notification’s 64-bit word:

notification.bits |= signal_bits;

This never blocks. Multiple signals accumulate.

Waiting

Wait(notification) atomically reads and clears the bits:

if notification.bits != 0 {
    result = notification.bits;
    notification.bits = 0;
    return result;
} else {
    block until someone signals;
}

IRQ Delivery

Hardware interrupts are delivered through notifications:

  1. A device raises an interrupt.

  2. The kernel’s interrupt handler signals the device driver’s notification.

  3. The driver, blocked in Wait, is woken.

  4. The driver handles the interrupt and acknowledges it.

This means device drivers in SaltyOS are normal programs. They just happen to wait on a notification that the kernel signals when hardware interrupts fire.

Bound Notifications

A server often needs to handle both client requests (via endpoint) and async events (via notification) in the same loop. SaltyOS supports this with bound notifications:

  1. Bind a notification to your TCB: TCB_BIND_NOTIFICATION(my_tcb, notification).

  2. When you call Recv on an endpoint, the kernel first checks your bound notification.

  3. If the notification has pending bits, they are returned immediately — you never enter the endpoint queue.

  4. If the notification is empty, you block on the endpoint as usual.

  5. If someone signals your notification while you are blocked on the endpoint, you are woken immediately.

This lets a single server loop handle both clients and IRQs without polling.

What to Read Next