Google

Programming JanosVM

Todd L. Miller

  1. 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.

  2. 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.)

  3. 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.

  4. 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.

  5. 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.

  6. 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.