cycles, thus reducing the overhead of
this isolation mechanism and allowing it to be used at finer granularity
than a conventional process. The high
cost of processes on other systems encourages monolithic software architectures and plug-ins to extend system
behavior. On Singularity, programmers are able to encapsulate small
extensions to existing applications or
to the system itself in their own separate SIPs. Table 3 summarizes the cost
in terms of CPU cycles of a variety of
systems for creating a process and
communicating with the kernel and
another process. These operations are
far less costly on Singularity;
SIPs do not share memory. Data
structures shared between two pro-
cesses provide a simple, high-band-
width communication mechanism
requiring little forethought on the part
of the host. However, when a process
fails, the shared structure couples the
failure to the other process, support-
ing the conservative assumption that
the first process left the shared struc-
ture in an inconsistent state. 7 Shared
memory further opens each process
to spontaneous corruption of shared
state at any time by an errant or mali-
cious peer. By forbidding shared mem-
ory, Singularity ensures that process
state is altered by only one process at
a time; and
figure 4. normalized execution time comparing the overhead cost of software and hardware
process isolation mechanisms for a Web server running on Singularity. our experiments ran
on a 1.8Ghz amD athlon 64 3000+ system, starting with a pure software-isolated version of
Singularity, progressively adding hardware address-space protection.
unsafe code Tax
+ 37.7%
1. 40
+ 33.0%
+ 18.9%
– 4.9%
1. 20
1.00
Safe code Tax
+ 6.3%
0.80
0.60
0.40
0.20
0.00
SiPS without
runtime
checks
SiPS in
physical
memory
SiPS in one
virtual memory
address space
Web server in
separate
address space
Web server in
ring 3
address space
all SiPS in
separate
address spaces
figure 5. message exchange across a channel; message ownership passes from Process 1
through a channel to Process 2.
Process 1
Process 2
Process 3
…
Page 23
Page 24
Page 25
Page 26
Page 27
…
it (see Figure 5). This semantic prevents SIPs from sharing the memory in
a message while allowing for efficient
communications, as code cannot distinguish communication in which a
message is copied from communication in which a pointer to the message
is passed among the SIPs. The receiving SIP should still validate message
parameters but need not worry about
their asynchronous modifications.
Each channel is annotated with a
specification, or “contract,” of the content of each message and the allowable
sequence of messages. For example,
the following code is part of the contract for a channel to Singularity’s TCP
service, defining the legal messages
that can arrive at the service when a
socket is connected:
public contract TcpSocketCon-tract {
...
state Connected : {
Read? -> ReadResultPending;
Write? -> WriteResultPending;
GetLocalAddress? ->
IPAddress! -> Connected;
GetLocalPort? -> Port! ->
Connected;
DoneSending? -> ReceiveOnly;
DoneReceiving? -> SendOnly;
Close? -> Closed;
Abort? -> Closed;
}
state ReadResultPending : {
Data! -> Connected;
NoMoreData! -> SendOnly;
RemoteClose! -> Zombie;
...
}
If, for example, the service receives
a Read message from a client, the
contract transitions to the ReadResultPending state, where the service
is expected to respond with a packet
of data or a status or error indication.
Singularity’s compiler statically checks
the code that sends and receives messages on a channel, ensuring it obeys
the contract.
One objection to SIPs and channels is they make writing software
more difficult than shared data structures and procedural APIs. Channel
contracts clearly require forethought
for designing and specifying an interface, which is a good thing. In practice,