![]() | ![]() |
|
||||||||||||||||||||||||||||||
|
ProcessesThe process module provides the following facilities:
A process calls create() to start another process and calls terminate() when it is finished execution. yield() gives up to processor to another process. dispatch() is called from within the kernel when a process wants to block. sleepOn() and sleepOff() are used to prevent the host processor from heating up when KModel enters a state where there is nothing to do until an interrupt occurs. Any Java class that subclasses kernel.Process can be started as a KModel process. kernel.Process is abstract and requires any instantiable subclass to provide a method public void main(). kernel.Process contains a reference to the interrupt mechanism and several overloaded convenience methods called SVC (supervisor call) that wrap primitive types in reference types before interrupting into the kernel. To illustrate the use of SVC and kernel.Process, "Hello, World" is written like this:
"Echo" is written like this:
The control context for user.Process is provided by kernel.ProcessWrapper, which is a Java Thread. ProcessWrapper provides an association between kernel.PCB, which is not a thread and is not visible outside the kernel package, and a subclass of user.Process. ProcessWrapper's run method, which calls Process's main() method, also manages the startup and termination protocol for a process.
A newly created process should begin it's existence as if it was blocked on exit from the kernel, like all the other processes in steady state. ProcessWrapper.run() guarantees that waitKernelExit() will be called to achieve this condition. When it's main() method returns, the process makes a supervisor call to invoke process termination. Note that the user process could have done this itself, using the same supervisor call. The kernel method to create processes uses the Java class loader to construct a class from a name provided as an argument. Instances of PCB and Process Wrapper are also created. Then the thread is started and control passes to the run() method in Process, as shown above.
If the class name passed in is not derived from kernel.Process, a cast exception will occur, which is handled by the exception handler. As an alternative to this design, kernel.ProcessWrapper could be redesigned so that the instance creation occurs in its run method. This keeps some nasty exception-handling code out of the kernel and back on the user side, where it belongs perhaps more reasonably. Although the new process begins executing as a Java thread immediately, it's PCB is placed in the ready queue and its state is marked as STARTING. This anomaly works itself out when ProcessWrapper.run calls pcb.waitKernelExit() to synchronize the process with all the other processes. Process termination is handled marking the process as TERMINATED and dispatching another process.
The process will end up permanently blocked when it returns from kernel via the softwareInterrupt mechanism, which calls PCB.waitKernelExit(). The terminate() method is a suitable place from which to initiate kernel-resource rundown. Process that block in the kernel call dispatch().
The readyQ is always guaranteed to have at least one process. The null process, which loops forever calling yield, exists for this purpose. Yield is implemented by placing the active process at the end of the ready queue and dispatching the process at the front of the queue.
There is a mild synchronization hiccup after calling dispatch. Until just prior to calling dispatch, the calling process is exactly the kernel.active process After returning from dispatch, another process is the kernel.active process. The calling process should exit the kernel as directly as possible. It, presumably, is blocked or terminated and will end up waiting in its PCB's waitKernelExit() method. The process that has been dispatched has been (Java) notify()'d and will proceed from its waitKernelExit() call, where it had been blocked. That same calling process still has the (Java monitor) lock on the InterruptMechanism, so the dispatched process is not eligible to execute until the calling process releases the lock (just prior to blocking). The hiccup is thus well managed and no race condition ensues. This design, in which processes block at kernel exit, precludes collaboration between processes inside the kernel. That is, it is not possible to block at an arbitrary point inside the kernel, dispatch another process, and resume the first process at the point it was blocked inside the kernel. To do this would require reconsideration of the techniques for kernel locking and process blocking. If all processes except the null process are blocked, the null process will idle in a yield loop until an interrupt occurs that results in some other process becoming ready. This yield loop consumes processor cycles on the host computer and results in 100% process utilization. To ameliorate this condition, the null process calls sleepOn() rather than yield(). sleepOn() will block the Java machine if nothing useful remains to be done. hardwareInterrupt() calls sleepOff() before dispatching any interrupt, resuming the Java machine.
waitQ is a queue that normally has at most one process. It is not empty only if there were no ready processes when sleepOn() is called. sleepOff, called implicitly by each interruptHandler(), redispatches the first process on the waitQ if one exists. It should always be the same as the one that had blocked on a sleepOn() call.
|
|||||||||||||||||||||||||||||
Last update 01/24/05
Copyright © Gerald Dueck
[=]