Prior to kernel design is the very concept that keys (capabilities) designate things and convey authority over those things.
Somewhat novel to Keykos was the plan to deliver small identifiable units of hardware function via keys.
Thus the page became an object whose use and protection were via keys.
The other principles might be claimed by any kernel design.
Independently we decided to provide persistence because we wanted the arrangements manifest by the distribution of keys to be uniform
across time scales rather than to provide hooks to reinstate these arrangements from “stable storage”.
Systems that take this conventional approach require that application and system code take steps to “stabilize” state here and there.
It is often difficult for the code to gain the perspective necessary to decide when to do this.
Many of the designs of code outside the kernel work on time scales from milliseconds to years.
A uniform stabilization policy would make them unusable on the millisecond scale and the opposite decision makes them unusable on the time scale.
Checkpoint and restart was non-trivial to do but it was trivial to specify!
It simplified so much code outside the kernel.
- Deliver hardware function as best you can without attenuating it.
Minimize the opportunity of the applications programmer to say “I could do it better on the raw hardware.”.
“Faster?” perhaps — “better?” No.
- Make the hardware safe for sharing.
This comes under the term “multiplexing”.
This includes protecting one program from another.
- Allow whatever communications are mutually desired.
That is not contrary to confinement—we will weasel around later.
- Provide whatever bottoming out services are required logically or for performance reasons.
Checkpoint logic and the decision to rely on it, dictated very reliable storage.
Thus mirroring (RAID) in the kernel.
There is just one explicit kernel invocation: to invoke a key.
On specific machines there were a few other special cases such as:
Other events that cause the kernel to start running are:
- Priv Op Emulation on 370
- We wanted to run IBM’s CMS system, in an interactive development environment.
CMS uses a few privileged instructions frequently which the kernel emulated, if options were selected via the domain key.
This was purely for performance.
The domain keeper could do this but more slowly.
- Compare and Swap on the 88K
- The 370 had taught us that the compare and swap instruction was very useful.
The 88K lacked such an instruction and so we allocated an invalid 88K opcode for that function which we provided in the kernel.
This was more for convenience than performance.
Each of these events might influence which domain would next run when the event had been dealt with.
- Timer interrupt: which is used to multiplex use of the CPU by domains (time slicing),
and also provide meter function with which to do coarse scheduling.
- Memory faults: which might result in mapping a page already defined by the domain’s memory tree, perhaps preceded by fetching from disk, or alternatively invoking a segment keeper.
- I/O interrupts: which mark the end of a sequence of autonomous operations that run asynchronously with the CPU to transfer data into and out of the system.