Most objects (entities, things) will be used locally. Therefore, most names of objects used will be names of local objects. Name resolution should, therefore, be most efficient for local names. This implies that local names should be shortest and suggests that names of local objects should normally be near to the root of the naming tree.
This, it must be clear, is a deviation from a trend towards using global name spaces. In a singly rooted global name space, the shortest path names refer to countries or organizations; it is rare that we wish to name those by themselves. The most widely claimed advantage of a global name space is that objects have the same name anywhere and that this facilitates sharing. What actually facilitates sharing much more is the proper use of naming conventions: One can often guess somebody's electronic-mail address, one looks for TeX macro files in subdirectories of /usr/local/lib or /usr/lib, one gives C source code files a `.c' extension. If the conventions are disobeyed, programs fail.
By using naming conventions properly, one can create name spaces that are only global in the sense that any object anywhere can be named, but not necessarily by the same name everywhere. The root of the naming tree can be the most local object and longer path names generally name objects further away. Conventions must be used to allow object sharing and there is no reason why one convention could not be the use of a subtree named /global for global names.
This sort of naming is used in Plan 9 from Bell Labs. <.pike name spaces plan 1993.> have already put forward some of the arguments for naming conventions being more important than global name spaces. Our naming mechanisms have been heavily inspired by those of Plan 9 as shall become clear.
Every process starts up with a built-in name space. Usually, this name space is inherited from a parent process and is at least partly shared with other name spaces. The name space consists of a local name space which names objects local to the process, and mounted name spaces which name objects external to the process. The mount point of a mounted name space is a local object with a connection to a name space in another process. Name resolution in mounted name spaces takes place by making name-lookup requests through the connection to the other process. The result of this resolution is an object handle. Using an object handle, objects can be accessed through their methods. The precise manner in which methods are invoked depends upon the ``domain relation'' between invoker and object. If they share a protection domain then the invocation is a procedure call; when they are in the same address space but different protection domains (for example on the same Nemesis machine) invocation is by protected call; and when in different address spaces invocation is performed by remote procedure call.
When making an invocation there is always code at the invoker's end that depends on the call interface. In the case of a local procedure call, this interface-dependent code is generated by the compiler. In the case of system calls it is loaded from a library and in the case of remote procedure call it is generated by a stub compiler and linked with the rest of the caller's code.
Client stubs for far-away objects may do more than just transport call parameters to the remote objects; they may, for instance, perform caching so that there is no longer a one-to-one mapping between client calls to the stubs and calls to the remote objects. Such intelligent stubs are referred to as agents or clerks.
When objects can migrate, for instance, to where they are accessed, the interfaces to them may change. This means that the interface with which calls are to be made is not always known a priori; the calling code depends on where the object is found when it is invoked.
Early distributed systems solved this by using the most general invocation method always: remote procedure call. This is not an optimal solution, especially now that dynamic linking can be used to invoke optimal code for the kind of call to be made in the case at hand.
An object-naming mechanism can be used to make the mechanism whereby object-interface code is loaded transparent. In our model, the resolution of the name of an object results in a handle. This handle is essentially a pointer to the interface to the object. For our handles we use maillons [.maisonneuve references as chains of links.], which consist of an opaque, fixed-size, object reference and a pointer to a function that returns the address of the interface when called with the reference as argument. The extra level of indirection provided by the maillon allows connections to objects to be set up, or objects to be fetched before their first invocation, but in the most common case -- the object is already there and ready to be invoked -- the maillon imposes very little overhead.
Object handles are first-class objects in that they can be passed as arguments in local and remote procedures. Passing an object handle for a local object to a remote process has the side effect of creating a connection through which the object can be invoked remotely.
The Pegasus remote-procedure-call mechanism is based on ANSA's RPC and layered on MSNA [.mcauley phd.]. The Multi-Service Network Architecture is a protocol hierarchy for ATM networks that also caters for continuous-media transport.