The structure of a Stella system is highly pragmatic. There are three aspects of the structure that have a major influence on the flexibility, the efficiency and the robustness.
Not all computers support the notions of privilege and protection. If, however, the processor provides automatic privilege transitions and privilege related protection mechanisms, Stella can use these to improve system security.
Because the operating system itself can access any resource, it is not enough to prevent applications from accessing protected resources directly. For this type of protection to work, it is necessary for the operating system to check the parameters of all calls that can potentially modify memory locations. It must, for example, be impossible to request a "read from file" operation into an area on memory that should not be accessible to the application. The effect of reading a file on top of the system variables would be just as disastrous as if the application overwrote the variables itself.
A normal operating system call performs, therefore, two preliminary operations.
Code that is already executing in operating system context (for example a device driver) will not need to re-establish the operating system context but it may be wise to have the parameters checked (the parameters may have been supplied by an application).
Calls from one operating system module to another, and calls from critical event handlers, do not (or should not) need any parameter checking.
Where the processor does not support levels of privilege, this is emulated in software. While this does give any real protection, it provides a uniformity of concept across the range of Stella modules and allows external monitoring hardware to be used to trap erroneous accesses during software development.
For high performance systems, time critical tasks will usually execute in privileged mode, thus by-passing the protection mechanisms in the interests of efficiency.
On the other hand, even in low cost systems, parameter checks can be useful. Some Stella modules not only check the parameters but also check the integrity of the operating system data structures that will be used. When developing software on low cost systems without protection hardware, there two reasons for using such operating system entries.
-R | Reduced security is used mainly for "live" cost critical systems. |
-N | Normal security protects against common programming errors. |
-H | High security provides more rigorous checks than normal. |
-P | Paranoid security is principally for testing low cost systems without hardware protection. The integrity of the operating system data structures is checked before use. |
The earliest "multitasking" operating systems resolved this problem by simply "locking" the task switching while such operating system resources were being accessed. This approach is extremely simple to implement and it is very efficient. If, however, the operations on operating system resources are allowed to become too long, the system response can be degraded.
In "modern" operating systems such resources are often protected by some means of "mutual exclusion". In any system, this approach will always degrade the efficiency, the predictability and the reliability1 of the system by comparison with simple locking. In time critical systems, the side effects of this approach can be even worse2.
In a single processor computer, or on each individual processor of a multiprocessor computer, only one task can be executing any one time. This task will continue to execute, either until it requests a reschedule or else it is interrupted by a hardware interrupt. Normally, when the "server" or "handler" for the interrupt has completed, the interrupted task will continue. The interrupt handler may, however, request a reschedule.
If interrupt servers do not make operating system calls, then things are fairly simple: all the operating system data structures will be completely immune from access conflicts if a task accessing these data structures cannot be forcibly suspended.
In standard Stella modules this is ensured by making all accesses to operating system data structures in privileged mode. (If the processor does not have a privileged execution mode, the mode transition is simulated in software.) While a task is executing in privileged mode, a request for a scheduling operation by an interrupt server will not take effect until the task returns to application mode. The request is not "lost": it is merely delayed. For most systems, these delays are much smaller than other timing uncertainties implicit in the multitasking concept.
This privilege controlled scheduler "lock-out" does not apply to interrupt servers (or external event handlers) as they are themselves "privileged" so the immediate response to external events is not affected.
It would seem that external event (or interrupt) handlers cannot make operating system calls (because they are not locked out during critical operations). Not all operating system functions, however, are protected in this way: the most important functions for external event (or interrupt) handling are intrinsically safe. The facilities for event handling, message passing, queuing, buffering, releasing jobs, etc. can be used without "additional" protection.
This approach creates a two-level system.
This restriction on asynchronous tasks is not completely arbitrary. There are other reasons for system designers to avoid making long operating system calls within interrupt handlers. In practice, therefore, this approach ensures that the most response critical operations are not burdened by mechanisms for resolving access conflicts, while for less critical tasks these mechanisms remain very simple.
The Stella modules using this approach are called bi-level modules (suffix -B). These bi-level modules do not provide a guaranteed maximum latency time for scheduled tasks (the length of time for which the scheduler is locked is not fully defined), other Stella modules provide guaranteed maximum scheduler latencies.
There are two disadvantages to using Stella atomised modules.
Stella atomised modules guarantee maximum latency times much shorter than the "market leader" real time operating systems. They still do not, however, allow certain operating system calls to be made from interrupt servers.
If the processor has "non-maskable" interrupts, then the handlers for these interrupts will not be affected: there is no additional latency and they may not make "long" operating system calls.
Where the processor has a number of interrupt levels, the interrupt "mask" can be set to a predetermined level during atomised operations. All handlers for interrupts at a priority above the predetermined level are unaffected: there is no additional latency and they may not make "long" operating system calls.
In principle, different operating system facilities could have different predetermined interrupt masks. This would, however, not improve the worst case performance, so this facility is not provided.
Modules | Access to operating system facilities | Interrupt latency | Scheduler latency | System efficiency | ||
Scheduled tasks | Normal interrupt handlers | High priority interrupt handler | "Instructions" Normal/High priority | "Instructions" | ||
-B | Unrestricted | Restricted | Restricted | 6/6 | Undefined | 99% |
-A | Unrestricted | Restricted | Restricted | 6/6 | 25 | 90% |
-S | Unrestricted | Unrestricted | Restricted | 25/6 | 25 | 85% |