shap: [I apologize in advance for both the length and the depth of this note.] so do I
In Dresden, a group of us is discussing related issues since a while ...
I am convinced since a long time * that communication should *not* be addressed to threads, or - in other words - a service's thread structure should be completely transparent to clients. * that all resources exported by the kernel (address spaces, threads, memory, interrupt lines, ...) should have a local name (now in L4, only memory has local names -> virtual addresses). All types of resources provided by the micro kernel can be transfered from a "pager" to a "client" via IPC just like page faults are handled now in L4 (mapping and flushing). Then, all resources are initially owned by sigma0 and L4's elegant pager hierarchies can be easily extended to all (types of) resources. "Mapping data bases" are then needed for all types of resources as well. (We are discussing generalizations to the "sigma0" scheme as well. E.g., each pager might be able to provide a resource to the kernel to obtain a number of threads in exchange and then owns these threads. It then can act as thread pager for the clients. I leave further discussion of that out of this email to avoid confusion.)
We refer to that scheme as "generalized mapping". Here is an example for the usage: a server may discover that it would like to use an extra thread, it simply uses it by its local name ("start 57" or "set registers 57, ..."). If 57 is void (a void capability in EROS speak), a "thread fault" is discovered by the micro kernel and transformed to a message to the responsible pager who may or may not respond by providing a thread mapping.
(I see no benefit though of a "unified capability space". Memory "descriptors" are PTEs.)
IPC provides the means for communication between units of protection. Units of protection in L4 are address spaces. Thus, messages should be sent from one address space to another using local names for address spaces. Such "send mappings" (could be called "send capabilities" for address spaces) are all owned by sigma0, can be mapped and flushed. For example, sending a message to 57 results(if void) in a "send mapping fault" to the responsible pager who may or may not respond by providing a send mapping. Arbitrary message interception structures can be established that way. No need to consider thread structures of services from the point of view of any client, be it an "original" client or an interceptor.
(I see no benefit though of restricting communication to "calls on objects". User-level SW of course can restrict communication to such patterns, but why should a micro-kernel enforce such restriction? Dresden's version of an IDL compiler is designed to support arbitrary message patterns. User-land "objects" should remain a user-land issue.)
The question now is: how does a receiver identify the sender? J.S. says: no need to identify since there are capabilities. I disagree here: relying just on capabilities leads to much-too-fine grained clustering of protection units. From my point of view, we need to enable protection units to securely find out from which clients messages came in, or - in other words - to serve clients with fairly different rights.
In Dresden, we discussed many schemes by which a receiver can identify a sender, here are my two current favorites (alternatives): * Identification via local names for "Ports": each address space (unit of protection) has several ports. Ports are entrance points into the address spaces; threads of an address space wait for messages on ports; ports are resources provided by the kernel and handled at user level using mapping and flushing operations. A send mapping (for example send mapping with local name 57) has the form (address space, Port-Range) where "Port-Range" is an interval in the local name of the addressee. All threads within an address space can wait on all its ports. Senders are identified via local name of the port a message comes in. (Some tricky details of this scheme omitted here. "Ports" are close to MACH ports, notable differences: synchronous IPC, flushing, ...) * Identification via "Principal ID"s of senders. They are enforced by the kernel(as with physical thread ids in L4), but managed at user level. A send mapping (for example send mapping with local name 57) has the form (address space, Principal ID: LENGTH,VALUE) IPC may then send out a message M by calling: send (57, M). The kernel assigns "57.VALUE" to the first part of M where the "first part" has length "57.LENGTH". Again, send mappings are passed from pagers to clients. While doing so, LENGTH may increase, but not decrease. That way, senders further down a pager hierarchy can be restricted in their "Principal ID" space or - in other words - in which content the first part of messages may contain. Almost arbitrary user-level naming schemes can be provided. (I omit to discuss the "closed wait" problems that need to be addressed in this scheme.)
--hermann