oss

Plan 9 lets every process assemble its own world

7 sources 5 primary sources September 10, 2026

Text
Ten members of the Bell Labs Computing Techniques Research Department gathered around computer terminals in their office.

The Bell Labs Computing Techniques Research Department members who developed Plan 9, photographed among terminals, cables, manuals, and the machines their system was meant to compose. Foreground, from left: Dennis Ritchie, Dave Presotto, and Rob Pike; background, from left: Tom Killian, Allen Eisdorfer, Tom Duff, Phil Winterbottom, Jim McKie, Howard Trickey, and Sean Dorward. Nokia Bell Labs.[7]

Plan 9's decisive abstraction is not “everything is a file.” Unix had already made that phrase useful. The sharper move is that each process can receive a different, assembled world of files, and the trees in that world can be served by a kernel device, a user program, or another machine. This architecture follows the path from a private name space through bind and mount, across the 9P protocol, and back into an ordinary open, read, or write. The result is less like a fixed filesystem and more like process-scoped service composition.

That distinction matters because it locates Plan 9's modularity somewhere unusually concrete. A program does not ask a service registry where the network stack lives, negotiate a bespoke RPC client, and then carry that location through its own code. It opens a conventional path such as /net/tcp. The process's name space decides which implementation stands behind that name. Location independence is therefore not a slogan layered above the operating system; it is a property of pathname resolution.[1][2]

The tree is assembled, not merely mounted

A Plan 9 process sees one rooted hierarchy, but that hierarchy need not come from one disk or even one computer. mount takes a bidirectional file descriptor connected to a file server and attaches the server's tree at a chosen path. bind takes a tree already visible somewhere in the current name space and makes it visible somewhere else. unmount removes an attachment. None of these operations requires the server to be a storage device.[1][2]

The attachment can replace the directory already at that path, or it can be placed before or after existing entries. Stacking trees creates a union directory. Lookup searches its components in order; creation goes to the first component explicitly permitted to accept new files. Plan 9 can therefore construct /bin from architecture-specific binaries, shell scripts, and a user's private commands without teaching the shell about a search-path algorithm.[1]

The crucial scope is the name-space group. A child normally shares its parent's view, but rfork controls whether resources—including the name space—remain shared or are copied into a new group. The call rfork(RFNAMEG) lets a process disconnect its name space before rearranging it. A debugger can overlay yesterday's headers at /sys/include; a window can receive its own /dev/cons; a restricted job can be given only the executables and service interfaces it needs. The change is real for that process family and invisible to unrelated work.[1][2]

This is finer-grained than selecting a machine-wide root. It also separates convention from identity. /dev/cons means “the console for this context,” not one globally distinguished device node. /bin/date means the appropriate executable at the conventional path, even when the actual binary came from an architecture-specific tree. Programs keep stable names while the name space supplies the local meaning.[1]

9P gives every server the same narrow throat

Private name spaces would be a local composition trick without a common way to cross a server boundary. 9P supplies that way. A file server offers a hierarchy; a client negotiates a protocol version and maximum message size, authenticates if required, and sends Tattach with a client-chosen fid that will identify the served tree's root. Twalk supplies that existing fid, pathname elements, and another client-chosen fid to associate with the destination when the walk succeeds. The replies return qids for the objects reached. Topen, Tread, Twrite, and Tclunk then turn familiar file operations into request-and-reply messages.[3]

A fid is client-selected session state, not a pathname shipped with every read. A qid is the server's identity for an object, including a path component and version field; it lets a client distinguish the same object reached through different names from a different object later created under a reused name. Tags match concurrent requests with replies, so a server may postpone a blocked read and answer other work first.[3]

That small vocabulary is the common throat. Kernel devices can implement it through local calls. A user-space program can synthesize a tree and serve it over a pipe. A remote file server can answer the same operations over a network transport. Most applications never construct a 9P packet: the mount driver translates ordinary file-system calls at the point where a resolved path crosses into a served tree.[1][3]

The architecture does not claim that all resources are stored files. It claims they can expose useful file semantics. /proc/<pid>/status synthesizes text when read. Writing stop or kill to a process's ctl file changes kernel state. /net/tcp/clone allocates a connection; writing a connect command to its ctl file advances that connection's state; its data file carries bytes. A window system can serve a private mouse, screen, and console tree to each client.[2]

That is an important limit on the famous metaphor. The file interface and 9P standardize an access vocabulary while making naming, protection, and tool reuse consistent across services. They do not standardize the meaning of every control message. A program using /net/tcp still needs to understand the network service's file layout and state machine. Plan 9 removes a class of integration machinery; it does not remove domain protocols.

cpu moves execution without abandoning the terminal

The design becomes clearest when computation crosses machines. Plan 9's cpu command starts a shell on a CPU server, but it does not simply drop the user into the remote host's pre-existing environment. It reconstructs a suitable name space there and exports pieces of the terminal's local environment back to the remote process. The shell can use files close to the CPU server while its applications still see the terminal's console and graphical devices at their conventional paths.[1][2]

The inverse operation is equally revealing. import helix /net can place another machine's network interfaces at the caller's /net. Software above that path need not grow a “remote network” mode. In the Bell Labs paper's remote-debugging example, importing a remote /proc lets familiar process tools inspect another machine; the debugger derives the target architecture from the executable rather than assuming it matches the terminal.[2]

This is not transparent because the network is magically free. It is transparent because the dependency has been rebound before the application uses it. The process still encounters the server's permissions, availability, timing, and semantics. What disappears is location-specific application plumbing.

The failure surface follows the attachment

The elegance of a common interface can make three boundaries easy to miss.

First, visibility is not authority. A private name space can hide resources or expose a deliberately narrow synthetic tree, which makes it a useful confinement mechanism. But attaching a tree still involves user identity, optional authentication through an afid, and permission checks enforced by the file server. Rearranging names does not authenticate an untrusted peer, and a permissive server does not become safe because it was mounted into a tidy path.[3][4]

Second, simple request/reply I/O is sensitive to distance. The 2005 Linux implementation study found workload-dependent results: 9P2000 did well in its small-file PostMark test, while NFS benefited from write-back and looser read consistency on large static files.[4] A later RIT thesis treated sequential transfer as a particular weakness of unbuffered 9P exchanges and experimented with out-of-band streams to reduce round trips.[6] Neither result supports a universal “9P is slow” or “9P is faster” verdict. They show that protocol beauty does not cancel workload shape, message size, caching, or network latency.

Third, caching changes the contract. Linux's current v9fs client exposes transports including Unix sockets, TCP, virtio, RDMA, and USB gadget links, plus several cache modes. Its documentation warns that cache=loose may not validate values against the server and should be used only with an exclusive mount whose backing tree will not change underneath the client.[5] A path can look perfectly ordinary while consistency has become an operational choice.

These are not incidental footnotes. They identify the review questions for any Plan 9-shaped system:

The surviving idea is composition before execution

Plan 9 did not become the mainstream successor to Unix. Nokia Bell Labs' 2021 transfer of the software copyright to the Plan 9 Foundation was an act of stewardship for a system whose influence had outgrown its installed base.[7] Yet its architectural seam remains visible outside the original operating system. The Linux kernel still ships v9fs and documents 9P mounts over remote, local, virtual-machine, and embedded-device transports.[5] The 2005 USENIX work explicitly joined 9P to Linux private name spaces as a basis for experimenting with distributed resources.[4]

The durable lesson is narrower—and more useful—than “make every API a file.” Compose a process's dependencies before execution, give those dependencies stable local names, and make the boundary observable. Plan 9's tools do this with name spaces, 9P trees, and ordinary file operations. The model pays off when a small interface genuinely fits the resource and when operators can reason about authority, state, latency, and failure at each attachment.

The Bell Labs team in the cover photograph sits in a room crowded with terminals, cables, manuals, and different machines. Plan 9's audacious answer to that physical heterogeneity was not to pretend the room contained one computer. It was to let each process assemble the particular computer it needed.

Sources

  1. Rob Pike, Dave Presotto, Sean Dorward, Bob Flandrena, Ken Thompson, Howard Trickey, and Phil Winterbottom, “Plan 9 from Bell Labs” — system overview covering private name spaces, union directories, 9P, file services, and cpu.
  2. Rob Pike, Dave Presotto, Ken Thompson, Howard Trickey, and Phil Winterbottom, “The Use of Name Spaces in Plan 9” — primary design paper with mount, bind, rfork, /proc, /net, import, and remote-debugging examples.
  3. Plan 9 Programmer's Manual, “Introduction to the Plan 9 File Protocol, 9P” — message framing, tags, fids, qids, authentication, walking, I/O, and permissions.
  4. Eric Van Hensbergen and Ron Minnich, “Grave Robbers from Outer Space: Using 9P2000 Under Linux,” USENIX Annual Technical Conference, 2005 — independent implementation study, protocol analysis, namespace discussion, and workload benchmarks.
  5. Linux kernel documentation, “v9fs: Plan 9 Resource Sharing for Linux” — current transports, mount examples, cache modes, and consistency warnings.
  6. John Floren, “FTP-like streams for the 9P file protocol,” Rochester Institute of Technology thesis, 2010 — independent examination of sequential-transfer latency and a streaming extension.
  7. Nokia Bell Labs, “Plan 9 from Bell Labs in Cyberspace!” March 23, 2021 — development history, copyright transfer, and source of the Bell Labs team photograph.
Previous A digital score can travel without arriving unchanged

Recommended In oss

Matched by subject and format