This chapter describes the details of implementation of the general Grâl architecture on computers with "traditional" architectures, including single-processor sequential machines with RAM or ROM-stored programs, and loosely or tightly coupled multiprocessors.
On those machines Grâl processes look very much like the processes in traditional operating systems; i.e. every process has memory segment(s) with executable code and data, stack(s) and thread(s) of execution, as well as the set of "resources" (i.e. the context).
Since memory segments are not included into Grâl notion of process, they are assumed to belong to memory manager, which services the requests of the memory protocol for the memory segments residing in virtual memory.
Although individual memory stores and fetches are not visible to the system's kernel, attaching the segments of virtual memory to the processes conforms to the generic Grâl framework, i.e. the accesses to memory objects may be manipulated in exactly the same way as accesses to any user-defined objects.
The Grâl framework allows for easy and efficient simulation of the traditional message passing and Unix-style pipes, by passing accesses to memory objects from one process to another. Note that unlike the common message-passing microkernels Grâl kernel copies message data only once, and if the "source" of data was a device capable of DMA into the provided memory object, CPU will not do any copying.
The memory manager can provide both "empty" memory segments (initially filled by zeroes), or caches for other objects implementing the memory protocol (for example, disk files).
User programs perform operations on objects using system calls described in the subsequent chapters of this document. For efficiency, system calls can be chained into small programs, and the privileged mode kernel code is invoked to execute the "programs" using small per-thread stacks of accesses. Such chains of system calls are called vectors.
Vectors of system calls are executed sequentially, until the end, or a system call returning an error. On error condition the rest of the vector is skipped, and all accesses placed on stack by the previous system calls on the vector are destroyed. The special system call, ERR can be used to partition the vectors, to the effect that execution of the next sub-vector will be allowed when the current one failed.
Execution of a vector of system calls may be illustrated by the following pseudo-code:
procedure sys(vector) begin result = 0 loop: get next element of the vector if element == END then return result fi if element == ERR then store result in a user variable result = 0 goto loop fi execute system call (element) if error condition then result = error code skip until END or ERR unwind stack goto loop fi if system call changes result then result = result code fi goto loop end
All events in Grâl processes are purely synchronous (i.e. user thread must explicitly wait for events); there is no equivalent of Unix signals. Execution errors, such as illegal operation codes or protection faults cause unconditional termination of the process. Software signals can be simulated by having an additional higher-priority thread to wait on signal-like events and execute appropriate signal handlers.
In most cases a thread will wait on a single event by using an access associated with the event and a special waiting system call. All other system calls requiring presence of an event as a precondition for an operation will fail immediately if the event didn't yet occur.
To handle cases when single thread needs to select one event from a set two extensions to the basic scheme are provided: the multiple-event wait, providing a simple way to prioritize events; and groups.
Although groups look like objects (i.e. there are accesses to groups), they are not objects; accesses to groups cannot be used as arguments of transactions.