Every networking page so far dialed a port on an interface. But a great many
connections never leave the machine: a tool talking to its daemon, a health
probe asking a service how it feels, two halves of one deployment sharing a
box. For those, Mica’s net unit has a second kind of address — a name,
under the kernel’s own namespace — and everything else stays exactly as you
learned it. That “everything else stays” is the lesson of this page.
The example is examples/IpcRoad
in the tutorial repository.
Getting the file
make -C mica-container/examples/IpcRoad runIpcRoad
The name, listened and dialed
the service answered: heard: a line over a name
The name nobody serves
the dial was refused - nothing listens on that nameThe example, walked
A name instead of a port
l := ListenIpc("mica-ipcroad-demo") on fail use l;
...
c := DialIpc("mica-ipcroad-demo") on fail use c;ListenIpc claims a name; DialIpc dials it. The answers are the same
Listener and Connection every other page used, and every verb you know
— Accept parking, the connection traveling into its task by value,
WriteLine and ReadLine speaking UTF-8 on the wire — works unchanged.
When both ends live on one machine, the name beats a port on every count
that matters:
- Nobody picks a number. Ports are a shared 16-bit space you negotiate
with every other program on the box; names are yours to invent. A second
claimant of the same name is refused with
Eaddrinuse— no silent fight, no “address already in use” mystery at 3 a.m. with a port number and no suspect. - No interface is crossed. The bytes never reach a network stack’s outside face, so no firewall has an opinion about them.
- Nothing is left behind. The name lives in the kernel’s abstract namespace and dies with its listener: no socket file lingers in the filesystem for the next run to trip over, and a restart never needs a cleanup ritual.
The honest refusal
ghost := DialIpc("mica-ipcroad-ghost") on fail caught do
begin
case caught of
Econnrefused:
WriteLn(" the dial was refused - nothing listens on that name")
...Dialing a name nobody serves answers Econnrefused — by name, at once,
deterministically. A closed door on the local road is a fact the kernel
states plainly, which is exactly what a start-up ordering check or a health
probe wants: is my daemon up? is one dial and one case arm, no timeout
tuning, no guessing.
Who may connect
An abstract name has no file behind it, so no file permission guards it: any process on the machine could dial your name, and any could claim it first. So both ends of the IPC road check the other end’s user, reading the credentials the kernel recorded for the connection:
ListenIpcanswers your own user.Acceptreads each connection’s credentials and closes a connection from another user’s process where it takes it, unread. Your program never sees it;Acceptsimply goes on to the next connection.DialIpcreaches your own user. If a program of another user claimed the name first, the dial fails withEaccesrather than handing that program what you meant for your own server.
A service meant for every user of the machine says so at both ends: it
listens with ListenIpcShared, and its clients dial it with
DialIpcShared. Either side can ask who is at the other end. Peer(c)
answers a Credentials record with the process id and the user and group
ids the kernel recorded when the connection was made:
who := Peer(c) on fail use who;
if who.uid = GetEuid() then
WriteLn(" the peer runs as this program's user");A TCP connection has no process of this machine at its other end, so Peer
fails there with Einval.
Try it
1. Claim the name twice. Declare a second listener, b : Listener;,
beside l, add Eaddrinuse to the names imported from posix, and after the
ListenIpc line ask for the same name again and read its refusal:
b := ListenIpc("mica-ipcroad-demo") on fail caught do
begin
case caught of
Eaddrinuse: WriteLn(" second claim refused: Eaddrinuse")
else WriteLn(" second claim failed with code %lld", Ord(caught))
end;
end; second claim refused: Eaddrinuse(Declare b : Listener and import Eaddrinuse : posix.) The name space
polices itself, and the collision has a name the moment it happens — at
claim time, not at some later dial.
2. Watch the name die with its listener. Move the ghost dial to ask for
"mica-ipcroad-demo" — the name that was served — after the
CloseListener line:
the dial was refused - nothing listens on that nameSame refusal as the ghost. The claim ended when the listener closed; nothing lingered. That is the cleanup ritual you do not have to write.
Next
The networking shelf is now walked end to end: the server loop, the chat room’s fan-in, the worker pool’s fan-out, typed values over the wire, the client road, and this page. When your two endpoints are tasks in one program, the same ideas live closer to home — Channel streams is that story. Every example behind the series lives in the tutorial repository.