Comparing IPC and capability invocation
haertig at os.inf.tu-dresden.de
Sun Dec 7 16:10:12 CET 2003
shap: [I apologize in advance for both the length and the depth of this
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
* 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
(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.)
More information about the l4-hackers