ORG, ROI, pem - Specification 03/16/93
Peter Mueller
mueller@sc.ZIB-Berlin.DE
Wed, 17 Mar 93 10:06:45 +0100
Howdy!
In this mail I will give you a first specification of remote object
invocation. There were no (zero!) responses to my previous mail. I get
a little confused, have I said something wrong? Don't you have enough
time to think it over? Well, I actually assume, that some of you have
not received my mail and I've sent out an acknowledgement request.
This specification presents only my point of view. It is written in
present time so it's possible to use it as documentation when things
work. I will try to keep it up-to-date and to `broadcast' it in a
periodical manner (if there are no other opinions). I hope, you will
send me many suggestions, ideas, wishes, and negative or positive criticism
(which always should be constructive, of course :-).
As I pointed out in previous mails, I'm an advocate of a strict separation
between design/specification and implementation. So, the specification only
says what happens and not how it is actually done. Nevertheless, as most of
you are more interested in implementation details, I will give an example on
how ROI is involved in an application at the end of the specification.
And now happy reading,
Peter
----- Cut here -----
R e m o t e O b j e c t I n v o c a t i o n
Specification Release 0.00 (03/16/93)
1 Overview
==========
This specification will give an abstract view on how remote object
invocation is done in MOOSE. Remote object invocation (ROI) means to invoke
a method from an object, which is not sharing the invoker's address space.
Thus, `remote' in this context means still both invoker and invokee be
running on the same machine. (This restriction will disappear, when MOOSE
becomes a distributed OS. Then `remote' means not the same address space or
even on a remote machine.)
Usually ROI is used, when an object needs access to services provided
by other objects. Therefore the terms `client' and `server' are used to
distinguish between the invoking and invoked object. Clients use services
from servers. This allows a strict separation between these two
functionalities, although it is still possible to have objects, which are
both, client and server, at the same time.
2 Design goals
==============
The following points give an overview of desired characteristics for ROI.
Thus, ROI should be:
1. general
2. object location transparent
3. very efficient regarding communication costs
4. user-friendly
2.1 Generality
--------------
ROI must be provided for a variety of objects, which can be wide spreaded
among different applications as well as different contexts or privilege
levels.
2.2 Object Location Transparency
--------------------------------
Most objects within MOOSE are location independent; it is not definitely
clear where the object exist at a given time. Additionally, objects are
created dynamically in a non-deterministic fashion.
Mechanisms are needed which provide a mapping function from an object's
description to its actual location. These mechanisms are provided by a
special kind of server object, the NAME SERVER. Name servers know the
absolute locations of their objects and make them transparently available
to other objects.
2.3 Efficiency
--------------
As one design goal of MOOSE is to provide a highly dynamic system, where
components are added on the fly, ROI will be a highly used communication
mechanism. Almost all traditional operating system services are provided by
independent objects. Therefore ROI must be very efficient to minimize the
remote invocation overhead. This can only be reached if very efficient IPC
mechanisms exist. Thus the kernel must provide very fast IPC (which is to be
specified in another release).
2.4 User-friendliness
---------------------
Users and their applications should not be aware of ROI. If an application
needs to invoke methods of a remote server it should simply instance an
appropriate client object. All necessary ROI stuff should be done by creation
of the client, which then provides a transparent view to the servers'
methods.
Surely there must exist mechanisms, which allow to interfere in the ROI
process as much as the user want. Eg, it must be possible to ask a specific
name server for a server object's location.
3. Object Identification
========================
To give access to a server's method the server must be known to all
possible clients. Thus the client must have the functionality to identify
the right server. Identifying can be done in several ways, eg.:
1. by name
2. by description
3. by value
This specification will only handle the first one, nameing.
3.1 Nameing
-----------
A server can be unambiguously identified by its name. A `name' is a unique
identifier within a given context, its `scope'. Note that several scopes may
coexist at the same time, eg. each application may have its own name scope.
This facilitates that multiple objects with the same name may coexist, but
each one must reside in its own scope.
A server is now an object, which can be seen as a tuple, combining a name
with provided services. To make these services available, the server
publishes its name within the desired scope by contacting the appropriate
name server. This is illustrated in Fig. 1:
+----------+ Make `MyServer' public +--------------+
| MyServer |-------------------------->| MyNameServer |
+----------+ +--------------+
Fig. 1
Here the server `MyServer' makes its services available within the scope
of the name server `MyNameServer'. Each time a client asks `MyNameServer'
for `MyServer' it will return the server's location (Fig. 2).
+----------+ Ask for `MyServer' +--------------+
| |------------------------------>| |
| MyClient | | MyNameServer |
| | Return `MyServer' location | |
| |<------------------------------| |
+----------+ +--------------+
Fig. 2
Now `MyClient' is able to invoke all provided methods from `MyServer' as
if `MyServer' is a local object. All necessary message passing is done by
ROI and hidden from the user of `MyClient'.
3.2 Name scopes
---------------
A scope is created whenever a name server is started. Thus all known names
to a name server form the name server's scope. An application, which creates
a name server have the ability to build up its own scope, allowing it to
redefine names as needed. Eg, it's possible to `overwrite' an existing file
server to an application specific one, which always encrypt data on writing
and decrypt it when reading. The application's file server can then be an
inherited file server, which is specialized for encryption. Of course it can
use methods from the `traditional' file server.
One question arises: What is the first scope OR where is the location of
the first name server?
As almost all traditional operating system services are done by objects,
there's the need for an default name server (DNS). DNS is installed at system
startup, providing the first name scope, which holds information about, for
example, the file server, printer server or disk servers. DNS is the only
object which must be installed at a well known location (or: the kernel must
provide a service to get its location).
+-------------+ client request or server registration
| Application |----------+
+-------------+ v
| ^ +-----+
ask for | | | DNS |
DNS by | | +-----+
request | | |
| | return | bounded
| | DNS | location User level
...........|...|...........|...................................
v | | Kernel level
+-------------+ |
| Kernel |--+
| +---------+ |
| | Nucleus | |
+-+---------+-+
Fig. 3
A DNS location request solved by a kernel service is shown in Fig. 3. For
simplicity only two privilege levels are shown, user and kernel level. An
application, wishing to use services of a remote server object or to install
a new server in the default name scope, must first find out the DNS location.
It initiates a request to the kernel, which returns the current DNS location.
Then the application can contact the DNS as usual.
Providing a kernel service have the advantage, that access to the DNS
is location transparent. The system is free to place the DNS as it wishes.
It has the disadvantage, that a kernel request must be additionally done to
find out its location. But for flexibility the advantage outweights the
disadvantage.
All necessary communication stuff for finding the DNS location should be
encapsulated in clients or servers, respectively. If no other attributes are
specified the DNS should be used automatically. Of course, an application,
which creates its own name server can declare this server as its DNS. Then
this name server is used, if no further attributes are given.
3.3 Addressing
--------------
The name server is responsible to map a received name into an useful
object location. This can be done either direct or indirect. A direct mapping
function can calculate the location from the name, as shown in Fig. 4:
+------+ Mapping function +-------------------+
| Name |-------------------->| Object's Location |
+------+ +-------------------+
Fig. 4
This is usually done by makeing the object's location part of the name.
This has one great disadvantage. Consider the case, where several clients
exist, which all want to use services of one server. Then all these clients
will use the same name. For some reason the server must be migrated to
another location. Then all names are invalid.
A second approach is to map a name to an `address' which then can be
mapped to the object's location (Fig. 5). If the destination object migrates
only the address must be changed. All names will still be valid.
+------+ +-------------------+
| Name | | Object's location |
+------+ +-------------------+
| ^
| |
| +---------+ |
+-->| Address |--+
+---------+
Fig. 5
With this approach a name can be very small. They only have to provide
all necessary information to indicate an address.
----- Cut here -----
To all of you, who want to see a more `implementation' oriented version
I will now present examples, how an user should be able to use ROI
in his/her application. All definitions are specified in a syntax similiar
to those of C++. Note: This is still a conceptual model! At the end I will
present problems which arise at this layer.
Examples
========
Think of a server providing two services:
o doSomething()
Invoke a very funny procedure within the server. The service must
be called with no arguments.
o doSomethingElse(first, second)
Invoke another procedure within the server. This service must be
called with two arguments.
The designer of the server have to do two things: He first have to create
a server object and he secondly have to provide a client interface to use
his server object. (This should be done automatically, either from a
compiler or preprocessor.)
The server might look like this:
01: class MYSERVER : SERVER {
02: // private and/or protected stuff (not visible)
03: public:
04: MYSERVER(NAMETYPE name = "myserver") : SERVER(name) {}
05: ~MYSERVER() {}
06: doSomething();
07: doSomethingElse(FIRSTTYPE first, SECONDTYPE second);
08: };
09:
10: MYSERVER::doSomething() {
11: // do something
12: } // MYSERVER::doSomething
13:
14: MYSERVER::doSomethingElse(FIRSTTYPE first, SECONDTYPE second) {
15: // do something with first and second.
16: } // MYSERVER::doSomethingElse
17:
18: main() {
19: MYSERVER server;
20: } // main
What happens?
Lines 01-08: This is the definition of the server object's interface. There
should be an abstract SERVER object from which all servers are
inherited. The constructor should be responsible to publish the
server to an appropriate name server with a useful, hence, user
readable name ("myserver"). When no other arguments are given,
the default name server is used. (The case when a server with
the name already exist is not discussed any further. Yet.)
The destructor will be responsible to unpublish the server. This
means the name server will be informed that there is no more
server with this name.
Lines 10-16: Usual method definitions. These methods should be invoked by
ROI.
Line 19: This actually establishes a new server. As there are no
arguments given, the server uses the default name (in this case
"myserver") and the default name server. Now a new server is
available in the scope of the default name server.
The provided client interface should look like this:
01: class MYSERVER_CLIENT : CLIENT {
02: // private and/or protected stuff
03: public:
04: MYSERVER_CLIENT(NAMETYPE name = "myserver") : CLIENT(name) {}
05: ~MYSERVER_CLIENT() {}
06: doSomething();
07: doSomethingElse(FIRSTTYPE first, SECONDTYPE second);
08: };
09:
10: main() {
11: MYSERVER_CLIENT myclient;
12: FIRSTTYPE first;
13: SECONDTYPE second;
14:
15: // Initialisation of first and second.
16: myclient.doSomething();
17: myclient.doSomethingElse(first, second);
18: } // main
What happens?
Lines 01-08: Definition of the client's interface. The public part should
always look like the one from the corresponding server object.
The constructor will contact the name server trying to locate
a useful server. When no other arguments are specified the
default name server is contacted.
Line 11: In this line a new client is created. As there are no arguments
specified, the default server (in this case the server with the
name "myserver") is used. Its location was received by asking
the default name server.
Lines 16,17: In both lines methods from the server are called. These calls
are performed by ROI, invoking the methods in the server
object.
Note: There are no definitions of doSomething() and doSomethingElse(),
because both are executed remotely.
Inheritance should be possible:
01: class NEWCLIENT : MYSERVER_CLIENT {
02: // private and/or protected stuff
03: public:
04: doSomething();
05: };
06:
07: NEWCLIENT::doSomething() {
08: // do NEWCLIENT'S specialization
09: MYSERVER_CLIENT::doSomething();
10: } // NEWCLIENT::doSomething
11:
12: main() {
13: NEWCLIENT myclient;
14:
15: myclient.doSomething();
16: } // main
Line 09: This line invoke doSomething() by ROI. All other stuff is
performed locally.
Line 15: Call of specialized doSomething() method locally.
Note: doSomethingElse() cannot be called, because NEWCLIENT uses
MYSERVER_CLIENT as a private base class.
Inheritance should even be possible with servers. This can lead to a
hierarchy of servers or to a call chain from descendants to ancestors with
ROI.
Problems
========
What, when public methods of a server call private methods?
-----------------------------------------------------------
Private methods are able to change the server's state. There must be
mechanisms which avoid an influence from one client to another client's
point of view to this state. Thus, each client must see it's own server
state.
What, when a server chooses a name which is already occupied by another
server?
-----------------------------------------------------------------------
This leads to a name conflict, which must be solved. The name server will
deny the request and the server is not published. Thus, the provided services
are not available.
What, when a client cannot locate an appropriate server?
--------------------------------------------------------
There are two reasons for this problem: First, there's no server
available. Then the client can simply create a new server instance. This
instance will then publish itself to the name server, which was asked by the
client. In this case the client knows the server's location. There's no need
to ask the name server again.
Second, there's a server using the name, but which is not appropriate
because it have the wrong type. There must be a mechanism which ensures that
the returned server is useful for the asking client.