Next: 2.5.1 Use in multiprocessor
Up: 2 Execution Environments
Previous: 2.4 Blocking Model
The interruptible blocking execution model,
unlike the other models,
allows a component to be re-entered at arbitrary points
under certain conditions.
In the interrupt model,
there are two ``levels'' in which a component's code may execute:
interrupt level and process level.
(Note that in this context we use the term ``process''
only because it is the traditional term used in this context;
the components in fact have no notion of an actual ``process.'')
The interrupt model also assumes a one-bit interrupt enable flag,
which the component can control through well-defined callback functions
which must be provided by the client OS.
When the component is running at either level and interrupts are enabled,
the component may be re-entered at interrupt level,
typically to execute an interrupt handler of some kind.
To be specific,
the properties of the interruptible blocking model are as follows:
- Each component is a single-threaded execution domain:
only one (virtual or physical) CPU may execute code
in the component at a given time.
For example, on a multiprocessor,
only one processor may be allowed
to execute in a component set at a time at process level;
this can be handled by placing a global lock around the component.
(Different components can be allowed to execute concurrently,
as long as the client OS takes appropriate precautions
to keep them fully independent of each other.)
Similarly, if the host OS is preemptible,
then the OS must ensure that if a component is preempted,
then that component will not be re-entered in another context
before the first context has finished or entered a blocking function.
- Multiple process-level activities
may be outstanding at a given time in a component,
as long as only one is actually executing at a time
(as required by rule 1).
A subset of the callback functions provided by the client OS
are defined as blocking functions;
whenever one of these functions is called,
the host OS may start a new activity in the component,
or switch back to other previously blocked activities.
- The host OS supplies each outstanding activity with a separate stack,
which is retained across blocking function calls.
Stacks are only relinquished by a component
when the operation completes and the component's code returns
from the original call that was used to invoke it.
- Code in a component always runs at one of two levels:
process level or interrupt level.
Whenever the host OS invokes a component
through its interface,
it enters the component at one of these two levels.
Typically, some of the component's exported functions or methods
can be invoked only at process level,
while others can be invoked only at interrupt level,
and there may be a few that can be invoked at either level;
which functions can be invoked at which levels
is part of the component's interface
(its contract with the client OS),
and thus is defined in the component's description.
Typically, most of a component's entrypoints
can only be invoked at process level;
therefore, entrypoints for which no level is explicitly specified
can be entered only at process level.
- Both process-level and interrupt-level execution in a component
can be interrupted at any time
by interrupt handlers in the component,
except when the code has disabled interrupts
using osenv_intr_disable (see Section 6.15.1).
- When a component is entered at process level,
the component assumes that interrupts are enabled.
The component may temporarily disable interrupts during processing,
but must re-enable them before returning to the client OS.
Similarly, when a component is entered at interrupt level
(e.g., a hardware interrupt handler in a device driver),
interrupts are assumed to be initially disabled
as if osenv_intr_disable had been called implicitly
before entering the handler.
However, the component may re-enable interrupts,
at which point the client OS is allowed
to interrupt the component again with other interrupt-level activities.
- Interrupt-level activities must be strictly stacked.
In other words,
when the client OS interrupts a process-level activity in a component,
that interrupt-level activity
must be allowed to run to completion
before the client OS may resume the process-level activity.
Similarly, if an interrupt-level activity is itself interrupted,
then the most recent interrupt-level activity must finish
before the client OS may resume previous interrupt-level activities.
This constraint is generally satisfied automatically
if the client OS is running on a uniprocessor
and uses only a single stack
for both process-level and interrupt-level processing;
however, the OSKit components
do not require the client OS to use only a single stack
as long as it meets these re-entrancy requirements.
- Code in a component that may run at interrupt level
may not call blocking callback functions
provided by the client OS;
only nonblocking callback functions may be called at interrupt level.
Although on the surface it may appear
that these requirements place severe restrictions on the host OS,
the required execution model can in fact be provided
quite easily even in most kernels supporting other execution models.
The following sections describe some example techniques
for providing this execution model.
Next: 2.5.1 Use in multiprocessor
Up: 2 Execution Environments
Previous: 2.4 Blocking Model
University of Utah Flux Research Group