naff moss logo
kent logo

MOSS: A Mini Operating-System Simulator

[ introduction | documentation | download | compiling/running | technical | notes ]

1. Introduction

MOSS is essentially a minature Java-based operating-system simulator. Its main purpose is to allow a form operating-system programming within the Java/JVM environment.

Operating-system development is a non-trivial task. It is hard, not only because of the nature of the program (i.e. all the things an operating-system must do) and the language involved (typically C), but also because development takes a serious amount of time. Debugging an operating-system is hard, especially if the machine you were running it on is completely locked up.

User-mode operating-systems (for instance, User-Mode Linux) are substantially easier to debug, since debugging tools are generally available in the host environment -- more to the point, there is a host environment, that can trap failure of the user-mode operating-system in a useful way (as the host operating-system will see the user-mode operating system as a collection of ordinary processes/threads).

The middle-ground of real and user-mode operating-systems is, of course, virtual machines running real operating-systems. VMWare is probably the most well-known example, but there are others. These make the job of debugging easier, since you've potentially got access to a memory-image of the virtual-machine, but it's still hard. Xen, a virtual machine monitor, is also in this middle-ground.

2. Documentation

Most of the necssary documentation can be found in the form of `javadoc' produced HTML:

Documentation added 11/02/2004: here are the slides from the `extra' lecture I gave on MOSS (CO501 03/04), that serves as useful documentation (these are relatively old now, however):

3. Download

You should download and un-compress/un-tar one of the following archives:

4. Compiling and Running

A `Makefile' is included in the source archive. On a UNIX machine, such as `raptor', you can compile and run using the following commands (should be run in the directory where the Makefile is):

    raptor$ make
    ... output from make ...

The Makefile will compile and build all the necessary components of MOSS. Currently these are the various core classes and modules. The compiled modules are left in the `moss/modules/' subdirectory, such that they are not visible to the JVM by default. The Makefile also runs a small shell-script, `mkinventory', that generates the file `INVENTORY'. When MOSS starts up, it uses this file (containing host file-names and MOSS file-names) to populate the initial file-system.

MOSS (from version 0.2.2) is now arranged as a series of Java packages, "moss.kernel", "moss.fs", etc. To run the system, simply:

    raptor$ java moss/MiniOSSim

A successful start-up should (for version 0.2.2) look something like this:

    MOSS (Mini Operating-System Simulator) version 0.2.2 starting...
    MKernel starting...
    host system: Linux/i386 ver 2.4.23
    runtime environment: 1.5.0/Sun Microsystems Inc.
    virtual system: Sun Microsystems Inc.  Java HotSpot(TM) Client VM/1.5.0-b64
    CPU0 is a VCPU:JVM/1.5.0-b64/Linux/2.4.23
    CPU1 is a VCPU:JVM/1.5.0-b64/Linux/2.4.23
    hello world!  (from init) 0 args:
    creating MJavaConsole process..
    loading ramdisk driver...
    mounting root file-system... mounted type MObjFS
    initialising root file-system... done
    processing inventory for /bin, /modules and /lib... done
    mounting MProcFS on /proc... done
    mounting MDevFS on /dev... done
    mounting MHostFS on /host... done
    starting /bin/console... done (pid 2)
    UConsole (/bin/console) starting

leaving you at the console-prompt (generated by the `UConsole' process). From here you can do various, things. For example, listing directories:

    MOSS# ll /
    listing of /:
    drwxr-xr-x 0        1     .
    drwxr-xr-x 0        1     ..
    drwxr-xr-x 0        2     etc
    drwxr-xr-x 0        3     dev
    drwxr-xr-x 0        4     bin
    drwxr-xr-x 0        5     lib
    drwxr-xr-x 0        6     proc
    drwxr-xr-x 1024     7     host
    drwxr-xr-x 0        8     modules

The `ll' command isn't really -- it's an alias for `/bin/ls -l'. A list of aliases can be obtained using the built-in `alias' command:

    MOSS# alias -l
    ls: /bin/ls
    ll: /bin/ls -l
    ps: /bin/proclist
    cat: /bin/cat
    kill: /bin/kill
    die: /bin/kill TERM

When listing directories (`/bin/ls') in long format (`-l'), the first column indicates permissions and mode, the second column gives the size (in bytes), the third gives the inode number in that file-system, and the name in the fourth column. Short output (without the `-l' flag) just produces a list of names.

The `bin/' directory contains the various executables (programs and utilities) for MOSS -- copied over from the host's file-system. Currently (version 0.2.2), these are:

    MOSS# /bin/ls -l /bin
    listing of /bin:
    drwxr-xr-x 0        4     .                        
    drwxr-xr-x 0        1     ..                       
    -rwxr-xr-x 2176     17    cat                      
    -rwxr-xr-x 8050     20    console                  
    -rwxr-xr-x 813      21    helloworld               
    -rwxr-xr-x 917      22    klog                     
    -rwxr-xr-x 2394     23    kill                     
    -rwxr-xr-x 1314     24    loadmodule               
    -rwxr-xr-x 2977     25    ls                       
    -rwxr-xr-x 1666     26    mailrecv                 
    -rwxr-xr-x 1532     27    mailsend                 
    -rwxr-xr-x 1950     28    mkdir                    
    -rwxr-xr-x 1794     29    mount                    
    -rwxr-xr-x 2679     30    pipetest                 
    -rwxr-xr-x 1353     31    pipetest2                
    -rwxr-xr-x 2389     32    proclist                 
    -rwxr-xr-x 1803     33    semtest                  
    -rwxr-xr-x 1351     34    semtest2                 
    -rwxr-xr-x 916      35    timertest                
    -rwxr-xr-x 1598     36    umount                   
    -rwxr-xr-x 1977     37    wfln                     

The `loadmodule' utility is used to load MOSS kernel-modules. For example:

    MOSS# /bin/ls -l /modules
    listing of /modules:
    drwxr-xr-x 0        8     .                        
    drwxr-xr-x 0        1     ..                       
    -rwxr-xr-x 1298     9     logsvr                   
    -rwxr-xr-x 672      10    testmod                  
    -rwxr-xr-x 3565     16    winsys                   
    MOSS# /bin/loadmodule /modules/winsys
    module loaded, pid = 7
    moss.modules.KWinSys: module loaded and active!  path is [/modules/winsys]

The `winsys' module is the very start of some graphics for MOSS. Currently it produces a window with some basic text and responds to the user clicking on the window (draws an `X' at that location). The mailbox IPC mechanism of MOSS is used to communicate graphics events from the event-thread back to the main `winsys' process.

4.1. Compiling on Linux

If you're using Linux, the compile/run process should be exactly the same. I'd strongly suggest using raptor (or any Sun JVM) for this -- I'm pretty confident its JVM works. The same cannot be said for all JVMs. The `blackdown' Java I previously used on Linux worked without any problems. I'm currently using Sun's J2SE JVM/JDK on Linux, version 5.0. With later versions of Java, you may notice warnings about "unsafe/unchecked operations". These can be safely ignored (fixing these would break MOSS for older versions of Java).

4.2. Compiling on Windows (and other non-Unix platforms)

If you don't have `make', or can't run Unix shell-scripts, then the automated (make) build-system will not work. Instead you should follow these steps:

  1. Compile the various .java files by hand (the packaging structure of MOSS may make this akward, but start with `javac moss/').
  2. Update the `moss/modules/INVENTORY' file to reflect any changes in available modules (and additional class files).
  3. Combine `moss/modules/INVENTORY' with `INVEXTRA' to create the `INVENTORY' file that MOSS actually uses.

4.3. Compiling and Running using BlueJ

From version 0.2.1, MOSS should work in BlueJ. Unpack one of the archives above then load it as a BlueJ project. Compiling the top-level `MiniOSSim' class will cause most other parts to be compiled, except some of the file-systems and the modules. You'll have to do these yourself manually (or run make from the command-line whilst BlueJ is running -- it doesn't seem to mind).

To run MOSS, select the "void main (String args[])" method from the `MiniOSSim' class. This should launch MOSS in the BlueJ terminal window. If not all the class files are available, MOSS will report an error, but won't necessarily terminate. To shut-down MOSS, right-click the "barber's pole" and reset the virtual-machine.

Updated 19/12/2004: there's now a BlueJ specific class for starting MOSS: `BlueMOSS'. This allows slightly nicer control over MOSS, enabling you to poke around in the internal structures using BlueJ's object inspector. Creating an instance of this class will start MOSS in the BlueJ terminal window. Although this sort of works, it's not entirely perfect -- there are concurrency issues with how BlueJ interacts with the running MOSS. So, for the most part, I'd recommend running MOSS from the command-line ...

5. Technical

MOSS is comprised of various Java classes (organised into packages) that implement, rather simulate, the different parts of an operating-system. By far the most important of these are `MKernel' and `MProcess'. `MKernel' provides the `guts' of the system, dealing largely with process startup/shutdown and scheduling. `MProcess' provides the base for a `process' in MOSS -- that `MKernel' schedules. MOSS is now structured into Java packages, e.g. "moss.kernel", "moss.ipc", "moss.fs", etc., although some future restructuring may still take place.

To create a useable process, `MProcess' is combined with another class that implements either the `MUserProcess' interface, or the `MKernelProcess' interface.

The general model and inner mechanics of MOSS are quite UNIX-like. The Linux kernel source has definitely been an influence, in addition to another occam-based operating-system -- in development.

5.1. Java Threads and MOSS

Operating-systems ought to be multi-tasking, and we certainly want this for MOSS. The most practical concurrency provision is that of Java `Thread's, that really may end up running in parallel, depending on the JVM and underlying hardware. Thus, the `MProcess' class extends `Thread', such that it exists as its own thread of control -- and must interact with others using Java mechanisms (`wait()', `notify()', `notifyAll()', `synchronized', plus some thread control).

To simulate an operating-system effectively, there must be some reasonable level of control over the processes running inside it. In particular, we want to be able to start and stop processes. The Java `Thread' methods `suspend' and `resume' are deprecated, which quickly rules out free-running of all `Thread's.

In a `real' operating-system, the low-level process switching and other unpleasantries are usually implemented in assembler (often dressed up as pre-processor macros). We don't quite have this in Java -- and external code linkage is a non-solution. Instead, MOSS makes do with the Java provided mechanisms and some compromises.

5.2. MOSS Process/Processor Control

The `MProcessor' class provides a crude abstraction for a `virtual processor'. This is an entirely passive object -- not a thread. Internally, `MKernel' maintains two arrays -- one of running processes, indexed by virtual CPU; and one of MProcessor references, indexed by the virtual CPU number as well. The scheduling of `MProcess's is done in such a way that only one runs on a given virtual CPU at any one time.

The implementation of process scheduling in MOSS is not pleasant. Once things are up and running, most interactions with the `MKernel' are done in the context of an `MProcess', that must be running on a virtual CPU. The context switch (basically) involves `notify()'ing the next process, then `wait()'ing the current one. There is a potentially brief overlap in execution between the two operations (essentially two `MProcess's running on the same virtual CPU), but it is handled safely inside `MKernel'.

Put another way, the context switch involves waking up the next process (an `MProcess' and thus Java `Thread'), just before the current one sleeps. You can't switch to yourself this way, but that's not really a problem..

The mechanisms dealing with process startup and shutdown are also contained within `MKernel', with some related start-up code in `MProcess'. The code that handles idling and wakeup of virtual CPUs is also in `MKernel'.

5.3. User Processes

`programs' in MOSS are classes that implement the `MUserProcess' interface (small), and run as separate Java threads. Interaction with the `operating-system' is done using the `MPosixIf' class, that provides the necessary glue.

As the name implies, `MPosixIf' provides a POSIX style operating-system interface. Only a small portion of the general POSIX interface is implemented, but enough works for the purposes of experimentation. `MPosixIf' also provides a number of MOSS-specific methods, such as `forkexec', that provides a combined "fork and execute".

5.4. Device Drivers

Device-drivers in MOSS (largely virtual/simulated), are either passive, active, or a combination. Passive device-drivers implement the `MFileOps' interface, that describes various input/output and device-control routines. The UNIX-style generalisation is used here -- "everything is a file". Such device-drivers are always executed in the context of a (user) `MProcess'.

Active device-drivers, on the other hand, exist as active `MProcess's, scheduled along-side user processes. The difference being that they implement the `MKernelProcess' interface (not `MUserProcess'). On their own, active device-drivers are not entirely useful -- unless they interact purely with the kernel, or allow user-processes access through other means (e.g. message-passing queues). Thus, device-drivers are free exist as two halves -- an active portion that runs as an `MProcess' (Java thread), combined with a passive `MFileOps' interface that runs in the context of user-processes (different Java threads).

When combined in this way, synchronisation between the `MProcess's involved must be done with care. Because their execution is potentially serialised (by a virtual processor), use of `wait()' and `notify()' could result in deadlock -- the only place an `MProcess' should sleep is in `MKernel'.

For some things, the `level' of concurrency given by an `MProcess' isn't enough. Virtual hardware is a good example: real hardware is aynchronous; so it makes sense that some virtual hardware might need to behave this way too. To achieve this, a passive device-driver can start its own Java `Thread's, providing it manages them correctly -- and doesn't break MOSS. The `MJavaConsole' process is a good example of this. This class provides the basic terminal input/output for MOSS, connecting to the Java `' and `System.out' streams. To handle the keyboard (standard-input), a separate thread is started internally, whose purpose is to read something then write it into a buffer in the `MJavaConsole'.

This sort of programming can complicate things, however, since regular `wait()'s and `notify()'s must be used to synchronise the threads. To simplify things slightly, a `Semaphore' class is available, that implements a standard semaphore using wait/notify. A message-passing mechanism is available too -- enabling a MOSS process to sleep in `MPosixIf.recvmsg()' and be woken by an asynchronous Java thread calling `MMailBox.sendmsg()' (the use of such direct calling is not for MOSS processes).

5.5. File System

MOSS has a Unix-style file-system: it starts at the root `/' from which everything else (including other mounted file-systems) descend. On startup, the "object file-sytem" (`MObjFS') is initialised and mounted at the root (initially being completely empty). A selection of files are then copied from the host environment into this object file-system (described in Section 4). These files provide various utilities, kernel-modules and "libraries" for MOSS.

The process execution mechanism in MOSS allows a call to, for example, `MPosixIf.forkexec ("/bin/ls", ...);'. The `MExec' class (in conjunction with process-creation code in `MProcess') loads these "programs" (actually Java classes) from the MOSS file-system and instantiates them as new MOSS processes. If a program (or module) loaded this way requires additional classes (e.g. private inner-classes), then `MExec' will attempt to load these from the MOSS `/lib/' directory.

In addition to the object file-system, MOSS also provides a process file-system (`MProcFS'), a device file-system (`MDevFS') and a host file-system (`MHostFS'). The host file-system makes visible the host's root file-system from within the MOSS; by default this is mounted on `/host/'. The process and device file-systems are mounted on `/proc/' and `/dev/' respectively.

6. Notes

MOSS is certainly not complete, although many of the major functional units are now in place. However, there are some things to be aware of:

I am, of course, happy to receive bug-reports, patches and other comments regarding MOSS :-).

Valid CSS!

Valid XHTML 1.1!

Last modified 16th January 2005
This document is maintained by Fred Barnes