
|
Programming JanosVM
Todd L. Miller
-
Orientation
JanosVM is not a Java runtime. It executes Java bytecodes, but it
does not, and does not aim to, provide the full Java API.
-
Teams
JanosVM implements protection using teams, which are like unix
processes, in that they can cause segmentation violations by writing into
another team's space, but unlike processes, in that they don't have strong
execution controls. Creating a new team creates garbage-collection and
finalizer threads, but no application threads; you have to provide them on
your own. Think `thread migration' and that you need to provide somewhere
to which they can migrate.
Teams are characterized by their independent (although not
disjoint*) classloaders, which are in turn characterized by their
classpath, classgroup(s), and viewgroup(s). Teams can be assigned any
subset of the classpath passed to the initial invocation of the JanosVM.
Teams are required to have a 'view on' the kernel classgroup, that is, be
assigned a viewgroup defined within the kernel classgroup. (Both are
defined in the .config file(s) JanosVM requires to run.)
-
Class and View Groups
A classgroup is a group of loosely related classes intended to be
shared among a group of teams. The kernel classgroup consists of the
minimum set of classes required to get anything done (e.g. those specified
in the VM or language specifications, like java.lang.Object and String),
and is `imported' into every team. (Those classes listed to be in kernel
classgroup are `exported' by the kernel team; currently, there are more
(over twelve dozen) classes in the root.config than are strictly
necessary, because it's convenient and nobody has spent the time to clean
things up.) Similarly, a classgroup intended for, say, a multi-team web
server, would include the classes of the objects in intends to share
between teams. Classgroups operate below class loaders, to ensure that no
potentially confusing or limiting classloader magic is necessary.
Viewgroups are attatched to a classgroup, and determine the
mapping from actual (in the .class file) names to in-team names (as
specified by the .config file). For example, JanosVM's java.lang.Thread
is not compatible with the Java API, but is required to be in the kernel
classgroup. The solution is to use a viewgroup to rename (remap, `export
out of the way') JanosVM's java.lang.Thread, perhaps to
janosvm.lang.Thread. Because the classgroup (and its associated
viewgroups) work below the classloader, teams run in this viewgroup will
see janosvm.lang.Thread instead of java.lang.Thread; a Java API-compliant
java.lang.Thread could then be implemented with calls to
janosvm.lang.Thread.
-
Importable and Exportable
The JanosVM API defines two classes, Importable and Exportable,
which are used for cross-team object sharing. Exportable objects reside
in one team, and Importable objects point at them from another. The two
teams must share (that is, use the same classgroup to reach; that is, one
must export and the other import) the class of the Importable, and the
transitive closure of the classes which it statically references. This
typically includes the class (object) that it wraps (somethingBackEnd);
otherwise, you get ClassCastExceptions, because the teams use different
classloaders. A team's classgroups are set for its lifetime at creation.
You can only export classgroups that were defined ahead of time (in the
config file), before JanosVM was started, though this is an implementation
issue, and may change.
-
Shared Classes
As mentioned above, some classes are (can be) shared between
teams. This means that they also share the statics associated with that
class. However, only the team which initially loaded the class can write
to its object (reference, non-primitive) statics. Usually, one will wrap
access to this object in a method which switches to its `parent' team,
clone its input to the static (or from the static to its output), and
return.
-
Creating New Teams
A team can create a new team by calling Team.create(). By calling
switchTo() on the returned TeamHandle, the current thread can enter the
new team. (This is where you must divest yourself of the process
metaphor! The new team does NOT have its own thread!) All the code in
the new team must be accessed via reflection, or from a shared class, or
it will result in a segmentation fault (of one kind or another).
Typically, then, the first thing the creating team will do is spawn a
thread in the new team which uses reflection to load the class and invoke
the method that was the reason for starting a new team. Arguments passed
across the switchTo() must typically be cloned, since most methods assume
mutable arguments, and will therefore cause segmentation violations when
they write to one; or be Exportable (and exported, by calling
exportObject() on a TeamHandle) in the creating team, and Importable (and
imported, by calling ImportObject on a TeamHandle) in the created team.
Typically, the exporting team will use a construct like
ExportableSubClass myExportedObject = ... ;
Team.current().exportObject( "identitifier", myExportedObject );
and the importing team a construct like
ImportableSubClass myImportedObject = ... ;
Team.current().importObject( myImportedObject, "identitifier" );
* Strictly, they begin disjoint, but the requirement for them to share the
identity/kernel view/class group limits this to the classes not in that
group.
|