Microkernel

Source: Wikipedia, the free encyclopedia.

Structure of monolithic and microkernel-based operating systems, respectively

In computer science, a microkernel (often abbreviated as μ-kernel) is the near-minimum amount of software that can provide the mechanisms needed to implement an operating system (OS). These mechanisms include low-level address space management, thread management, and inter-process communication (IPC).

If the hardware provides multiple

user space.[1]

In terms of the source code size, microkernels are often smaller than

MINIX 3 microkernel, for example, has only approximately 12,000 lines of code.[2]

History

Microkernels trace their roots back to Danish computer pioneer Per Brinch Hansen and his tenure in Danish computer company Regnecentralen where he led software development efforts for the RC 4000 computer.[3] In 1967, Regnecentralen was installing a RC 4000 prototype in the Zakłady Azotowe Puławy fertilizer plant in Poland. The computer used a small real-time operating system tailored for the needs of the plant. Brinch Hansen and his team became concerned with the lack of generality and reusability of the RC 4000 system. They feared that each installation would require a different operating system so they started to investigate novel and more general ways of creating software for the RC 4000.[4] In 1969, their effort resulted in the completion of the RC 4000 Multiprogramming System. Its nucleus provided inter-process communication based on message-passing for up to 23 unprivileged processes, out of which 8 at a time were protected from one another. It further implemented scheduling of time slices of programs executed in parallel, initiation and control of program execution at the request of other running programs, and initiation of data transfers to or from peripherals. Besides these elementary mechanisms, it had no built-in strategy for program execution and resource allocation. This strategy was to be implemented by a hierarchy of running programs in which parent processes had complete control over child processes and acted as their operating systems.[5][6]

Following Brinch Hansen's work, microkernels have been developed since the 1970s.

mono-kernels
" to these new systems. New device drivers, protocol stacks, file systems and other low-level systems were being developed all the time. This code was normally located in the monolithic kernel, and thus required considerable work and careful code management to work on. Microkernels were developed with the idea that all of these services would be implemented as user-space programs, like any other, allowing them to be worked on monolithically and started and stopped like any other program. This would not only allow these services to be more easily worked on, but also separated the kernel code to allow it to be finely tuned without worrying about unintended side effects. Moreover, it would allow entirely new operating systems to be "built up" on a common core, aiding OS research.

Microkernels were a very hot topic in the 1980s when the first usable local area networks were being introduced.[citation needed]. The AmigaOS Exec kernel was an early example, introduced in 1986 and used in a PC with relative commercial success. The lack of memory protection, considered in other respects a flaw, allowed this kernel to have very high message-passing performance because it did not need to copy data while exchanging messages between user-space programs.[9]

The same mechanisms that allowed the kernel to be distributed into user space also allowed the system to be distributed across network links. The first microkernels, notably Mach created by Richard Rashid, proved to have disappointing performance, but the inherent advantages appeared so great that it was a major line of research into the late 1990s.[citation needed] However, during this time the speed of computers grew greatly in relation to networking systems, and the disadvantages in performance came to overwhelm the advantages in development terms.[citation needed]

Many attempts were made to adapt the existing systems to have better performance, but the overhead was always considerable and most of these efforts required the user-space programs to be moved back into the kernel. By 2000, most large-scale

OSFMK 7.3 kernel) with code from BSD UNIX,[10][11] and this kernel is also used in iOS, tvOS, and watchOS. Windows NT, starting with NT 3.1 and continuing with Windows 11, uses a hybrid kernel design. As of 2012, the Mach-based GNU Hurd is also functional and included in testing versions of Arch Linux and Debian
.

Although major work on microkernels had largely ended, experimenters continued development.[

assembly code
and relying on the processor to enforce concepts normally supported in software led to a new series of microkernels with dramatically improved performance.

Microkernels are closely related to

L4 microkernel
frequently finds use in a hypervisor capacity.

Introduction

Early operating system kernels were rather small, partly because computer memory was limited. As the capability of computers grew, the number of devices the kernel had to control also grew. Throughout the early history of Unix, kernels were generally small, even though they contained various device drivers and file system implementations. When address spaces increased from 16 to 32 bits, kernel design was no longer constrained by the hardware architecture, and kernels began to grow larger.

The Berkeley Software Distribution (BSD) of Unix began the era of larger kernels. In addition to operating a basic system consisting of the CPU, disks and printers, BSD added a complete TCP/IP networking system and a number of "virtual" devices that allowed the existing programs to work 'invisibly' over the network. This growth continued for many years, resulting in kernels with millions of lines of source code. As a result of this growth, kernels were prone to bugs and became increasingly difficult to maintain.

The microkernel was intended to address this growth of kernels and the difficulties that resulted. In theory, the microkernel design allows for easier management of code due to its division into

kernel mode. For example, if a networking service crashed due to buffer overflow
, only the networking service's memory would be corrupted, leaving the rest of the system still functional.

Inter-process communication

network protocol stacks
, file systems, graphics, etc.

IPC can be synchronous or asynchronous. Asynchronous IPC is analogous to network communication: the sender dispatches a message and continues executing. The receiver checks (polls) for the availability of the message, or is alerted to it via some notification mechanism. Asynchronous IPC requires that the kernel maintains buffers and queues for messages, and deals with buffer overflows; it also requires double copying of messages (sender to kernel and kernel to receiver). In synchronous IPC, the first party (sender or receiver) blocks until the other party is ready to perform the IPC. It does not require buffering or multiple copies, but the implicit rendezvous can make programming tricky. Most programmers prefer asynchronous send and synchronous receive.

First-generation microkernels typically supported synchronous as well as asynchronous IPC, and suffered from poor IPC performance.

]

In a series of experiments, Chen and Bershad compared memory

user space. Their results explained Mach's poorer performance by higher MCPI and demonstrated that IPC alone is not responsible for much of the system overhead, suggesting that optimizations focused exclusively on IPC will have a limited effect.[15] Liedtke later refined Chen and Bershad's results by making an observation that the bulk of the difference between Ultrix and Mach MCPI was caused by capacity cache-misses and concluding that drastically reducing the cache working set of a microkernel will solve the problem.[16]

In a client-server system, most communication is essentially synchronous, even if using asynchronous primitives, as the typical operation is a client invoking a server and then waiting for a reply. As it also lends itself to more efficient implementation, most microkernels generally followed L4's lead and only provided a synchronous IPC primitive. Asynchronous IPC could be implemented on top by using helper threads. However, experience has shown that the utility of synchronous IPC is dubious: synchronous IPC forces a multi-threaded design onto otherwise simple systems, with the resulting synchronization complexities. Moreover, an RPC-like server invocation sequentializes client and server, which should be avoided if they are running on separate cores. Versions of L4 deployed in commercial products have therefore found it necessary to add an asynchronous notification mechanism to better support asynchronous communication. This

signal-like mechanism does not carry data and therefore does not require buffering by the kernel. By having two forms of IPC, they have nonetheless violated the principle of minimality. Other versions of L4 have switched to asynchronous IPC completely.[17]

As synchronous IPC blocks the first party until the other is ready, unrestricted use could easily lead to deadlocks. Furthermore, a client could easily mount a

timeouts on IPC calls, which limit the blocking time. In practice, choosing sensible timeout values is difficult, and systems almost inevitably use infinite timeouts for clients and zero timeouts for servers. As a consequence, the trend is towards not providing arbitrary timeouts, but only a flag which indicates that the IPC should fail immediately if the partner is not ready. This approach effectively provides a choice of the two timeout values of zero and infinity. Recent versions of L4 and MINIX have gone down this path (older versions of L4 used timeouts). QNX avoids the problem by requiring the client to specify the reply buffer as part of the message send call. When the server replies the kernel copies the data to the client's buffer, without having to wait for the client to receive the response explicitly.[18]

Servers

Microkernel servers are essentially

daemon
programs like any others, except that the kernel grants some of them privileges to interact with parts of physical memory that are otherwise off limits to most programs. This allows some servers, particularly device drivers, to interact directly with hardware.

A basic set of servers for a general-purpose microkernel includes file system servers, device driver servers, networking servers,

display servers, and user interface device servers. This set of servers (drawn from QNX) provides roughly the set of services offered by a Unix monolithic kernel
. The necessary servers are started at system startup and provide services, such as file, network, and device access, to ordinary application programs. With such servers running in the environment of a user application, server development is similar to ordinary application development, rather than the build-and-boot process needed for kernel development.

Additionally, many "crashes" can be corrected by simply stopping and restarting the server. However, part of the system state is lost with the failing server, hence this approach requires applications to cope with failure. A good example is a server responsible for TCP/IP connections: If this server is restarted, applications will experience a "lost" connection, a normal occurrence in a networked system. For other services, failure is less expected and may require changes to application code. For QNX, restart capability is offered as the QNX High Availability Toolkit.[19]

Device drivers

Device drivers frequently perform direct memory access (DMA), and therefore can write to arbitrary locations of physical memory, including various kernel data structures. Such drivers must therefore be trusted. It is a common misconception that this means that they must be part of the kernel. In fact, a driver is not inherently more or less trustworthy by being part of the kernel.

While running a device driver in user space does not necessarily reduce the damage a misbehaving driver can cause, in practice it is beneficial for system stability in the presence of buggy (rather than malicious) drivers: memory-access violations by the driver code itself (as opposed to the device) may still be caught by the memory-management hardware. Furthermore, many devices are not DMA-capable, their drivers can be made untrusted by running them in user space. Recently, an increasing number of computers feature

IOMMUs, many of which can be used to restrict a device's access to physical memory.[20]
This also allows user-mode drivers to become untrusted.

User-mode drivers actually predate microkernels. The Michigan Terminal System (MTS), in 1967, supported user space drivers (including its file system support), the first operating system to be designed with that capability.[21] Historically, drivers were less of a problem, as the number of devices was small and trusted anyway, so having them in the kernel simplified the design and avoided potential performance problems. This led to the traditional driver-in-the-kernel style of Unix,[22] Linux, and Windows NT. With the proliferation of various kinds of peripherals, the amount of driver code escalated and in modern operating systems dominates the kernel in code size.

Essential components and minimality

As a microkernel must allow building arbitrary operating system services on top, it must provide some core functionality. At a minimum, this includes:

This minimal design was pioneered by

Nucleus and the hypervisor of IBM's VM
. It has since been formalised in Liedtke's minimality principle:

A concept is tolerated inside the microkernel only if moving it outside the kernel, i.e., permitting competing implementations, would prevent the implementation of the system's required functionality.[16]

Everything else can be done in a usermode program, although device drivers implemented as user programs may on some processor architectures require special privileges to access I/O hardware.

Related to the minimality principle, and equally important for microkernel design, is the separation of mechanism and policy, it is what enables the construction of arbitrary systems on top of a minimal kernel. Any policy built into the kernel cannot be overwritten at user level and therefore limits the generality of the microkernel.[12] Policy implemented in user-level servers can be changed by replacing the servers (or letting the application choose between competing servers offering similar services).

For efficiency, most microkernels contain schedulers and manage timers, in violation of the minimality principle and the principle of policy-mechanism separation.

Start up (booting) of a microkernel-based system requires device drivers, which are not part of the kernel. Typically, this means that they are packaged with the kernel in the boot image, and the kernel supports a bootstrap protocol that defines how the drivers are located and started; this is the traditional bootstrap procedure of L4 microkernels. Some microkernels simplify this by placing some key drivers inside the kernel (in violation of the minimality principle), LynxOS and the original Minix are examples. Some even include a file system in the kernel to simplify booting. A microkernel-based system may boot via multiboot compatible boot loader. Such systems usually load statically-linked servers to make an initial bootstrap or mount an OS image to continue bootstrapping.

A key component of a microkernel is a good IPC system and virtual-memory-manager design that allows implementing page-fault handling and swapping in usermode servers in a safe way. Since all services are performed by usermode programs, efficient means of communication between programs are essential, far more so than in monolithic kernels. The design of the IPC system makes or breaks a microkernel. To be effective, the IPC system must not only have low overhead, but also interact well with CPU scheduling.

Performance

On most mainstream processors, obtaining a service is inherently more expensive in a microkernel-based system than a monolithic system.

ring or CPU mode). In the microkernel-based system, the service is obtained by sending an IPC message to a server, and obtaining the result in another IPC message from the server. This requires a context switch
if the drivers are implemented as processes, or a function call if they are implemented as procedures. In addition, passing actual data to the server and back may incur extra copying overhead, while in a monolithic system the kernel can directly access the data in the client's buffers.

Performance is therefore a potential issue in microkernel systems. The experience of first-generation microkernels such as

L4 microkernel that through careful design and implementation, and especially by following the minimality principle, IPC costs could be reduced by more than an order of magnitude compared to Mach. L4's IPC performance is still unbeaten across a range of architectures.[23][24][25]

While these results demonstrate that the poor performance of systems based on first-generation microkernels is not representative for second-generation kernels such as L4, this constitutes no proof that microkernel-based systems can be built with good performance. It has been shown that a monolithic Linux server ported to L4 exhibits only a few percent overhead over native Linux.[26] However, such a single-server system exhibits few, if any, of the advantages microkernels are supposed to provide by structuring operating system functionality into separate servers.

A number of commercial multi-server systems exist, in particular the real-time systems QNX and Integrity. No comprehensive comparison of performance relative to monolithic systems has been published for those multiserver systems. Furthermore, performance does not seem to be the overriding concern for those commercial systems, which instead emphasize reliably quick interrupt handling response times (QNX) and simplicity for the sake of robustness. An attempt to build a high-performance multiserver operating system was the IBM Sawmill Linux project.[27] However, this project was never completed.

It has been shown in the meantime that user-level device drivers can come close to the performance of in-kernel drivers even for such high-throughput, high-interrupt devices as Gigabit Ethernet.[28] This seems to imply that high-performance multi-server systems are possible.

Security

The security benefits of microkernels have been frequently discussed.[29][30] In the context of security the minimality principle of microkernels is, some have argued, a direct consequence of the principle of least privilege, according to which all code should have only the privileges needed to provide required functionality. Minimality requires that a system's trusted computing base (TCB) should be kept minimal. As the kernel (the code that executes in the privileged mode of the hardware) has unvetted access to any data and can thus violate its integrity or confidentiality, the kernel is always part of the TCB. Minimizing it is natural in a security-driven design.

Consequently, microkernel designs have been used for systems designed for high-security applications, including

common criteria (CC) at the highest assurance level (Evaluation Assurance Level
(EAL) 7) has an explicit requirement that the target of evaluation be "simple", an acknowledgment of the practical impossibility of establishing true trustworthiness for a complex system. Again, the term "simple" is misleading and ill-defined. At least the Department of Defense Trusted Computer System Evaluation Criteria introduced somewhat more precise verbiage at the B3/A1 classes:

"The TCB shall [implement] complete, conceptually simple protection mechanisms with precisely defined semantics. Significant system engineering shall be directed toward minimizing the complexity of the TCB, as well as excluding from the TCB those modules that are not protection-critical."

— Department of Defense Trusted Computer System Evaluation Criteria

In 2018, a paper presented at the Asia-Pacific Systems Conference claimed that microkernels were demonstrably safer than monolithic kernels by investigating all published critical CVEs for the Linux kernel at the time. The study concluded that 40% of the issues could not occur at all in a formally verified microkernel, and only 4% of the issues would remain entirely unmitigated in such a system.[31]

Third generation

More recent work on microkernels has been focusing on formal specifications of the kernel API, and formal proofs of the API's security properties and implementation correctness. The first example of this is a mathematical proof of the confinement mechanisms in EROS, based on a simplified model of the EROS API.[32] More recently (in 2007) a comprehensive set of machine-checked proofs was performed of the properties of the protection model of seL4, a version of L4.[33]

This has led to what is referred to as third-generation microkernels,

virtualization as a first-class concern, novel approaches to kernel resource management,[35] and a design goal of suitability for formal analysis, besides the usual goal of high performance. Examples are Coyotos, seL4, Nova,[36][37] Redox and Fiasco.OC.[36][38]

In the case of seL4, complete formal verification of the implementation has been achieved,[34] i.e. a mathematical proof that the kernel's implementation is consistent with its formal specification. This provides a guarantee that the properties proved about the API actually hold for the real kernel, a degree of assurance which goes beyond even CC EAL7. It was followed by proofs of security-enforcement properties of the API, and a proof demonstrating that the executable binary code is a correct translation of the C implementation, taking the compiler out of the TCB. Taken together, these proofs establish an end-to-end proof of security properties of the kernel.[39]

Examples

Some examples of microkernels are:

Nanokernel

The term nanokernel or picokernel historically referred to:

  • A kernel where the total amount of kernel code, i.e. code executing in the privileged mode of the hardware, is very small. The term picokernel was sometimes used to further emphasize small size. The term nanokernel was coined by Jonathan S. Shapiro in the paper The KeyKOS NanoKernel Architecture. It was a sardonic response to Mach, which claimed to be a microkernel while Shapiro considered it monolithic, essentially unstructured, and slower than the systems it sought to replace. Subsequent reuse of and response to the term, including the picokernel coinage, suggest that the point was largely missed. Both nanokernel and picokernel have subsequently come to have the same meaning expressed by the term microkernel.
  • A virtualization layer underneath an operating system, which is more correctly referred to as a hypervisor.
  • A
    hardware abstraction layer that forms the lowest-level part of a kernel, sometimes used to provide real-time functionality to normal operating systems, like Adeos
    .

There is also at least one case where the term nanokernel is used to refer not to a small kernel, but one that supports a nanosecond clock resolution.[40]

See also

References

  1. ^ Herder, Jorrit N. (23 February 2005). "Toward a True Microkernel Operating System" (PDF). minix3.org. Retrieved 22 June 2015.
  2. ^ "read-more". Retrieved 20 December 2016.
  3. ^ "Per Brinch Hansen". IEEE Computer Society. Retrieved 13 September 2016.
  4. ^ Brinch Hansen, Per (2004). A Programmer's Story: The Life of a Computer Pioneer. Retrieved 13 September 2016.
  5. ^ Brinch Hansen, Per (April 1969). RC 4000 Software: Multiprogramming System (PDF) (Technical report). Regnecentralen. Retrieved 13 September 2016.
  6. S2CID 9414037
    .
  7. .
  8. .
  9. ^ Sassenrath, Carl (1986). Amiga ROM Kernel Reference Manual. Exec.{{cite book}}: CS1 maint: location missing publisher (link)
  10. ^ Jim Magee. WWDC 2000 Session 106 - Mac OS X: Kernel. 14 minutes in. Archived from the original on 11 December 2021.
  11. ^ "Porting UNIX/Linux Applications to Mac OS X". Apple. Retrieved 26 April 2011.
  12. ^
    S2CID 2867357
    .
  13. S2CID 7414062. Archived from the original
    on 13 January 2014. Retrieved 13 January 2014.
  14. .
  15. ^ .
  16. ^ .
  17. .
  18. ^ "Synchronous Message Passing". Retrieved 14 July 2019.
  19. ^ "The QNX High Availability Toolkit" (PDF). Archived from the original (PDF) on 24 August 2005.
  20. ^ Wong, William (27 April 2007). "I/O, I/O, It's Off to Virtual Work We Go". Electronic Design. Retrieved 8 June 2009.
  21. S2CID 14614148
    .
  22. .
  23. ^ Liedtke, Jochen; Elphinstone, Kevin; Schönberg, Sebastian; Härtig, Hermann; Heiser, Gernot; Islam, Nayeem; Jaeger, Trent (May 1997). Achieved IPC performance (still the foundation for extensibility). 6th Workshop on Hot Topics in Operating Systems. Cape Cod, MA, USA: IEEE. pp. 28–31.
  24. ^ Gray, Charles; Chapman, Matthew; Chubb, Peter; Mosberger-Tang, David; Heiser, Gernot (April 2005). Itanium—a system implementor's tale. USENIX Annual Technical Conference. Annaheim, CA, USA. pp. 264–278.
  25. ^ van Schaik, Carl; Heiser, Gernot (January 2007). High-performance microkernels and virtualisation on ARM and segmented architectures. 1st International Workshop on Microkernels for Embedded Systems. Sydney, Australia: NICTA. pp. 11–21. Archived from the original on 26 April 2007. Retrieved 1 April 2007.
  26. S2CID 1706253
    .
  27. ^ Gefflaut, Alain; Jaeger, Trent; Park, Yoonho;
    CiteSeerX 10.1.1.25.8376
    .
  28. .
  29. ^ Tanenbaum, Andrew S. "Tanenbaum-Torvalds debate, part II".
  30. ^ Tanenbaum, A., Herder, J. and Bos, H. (May 2006).
  31. .
  32. ^ Shapiro, Jonathan S.; Weber, Samuel. Verifying the EROS Confinement Mechanism. IEEE Conference on Security and Privacy. Archived from the original on 3 March 2016.
  33. ^ Elkaduwe, Dhammika; Klein, Gerwin; Elphinstone, Kevin (2007). Verified Protection Model of the seL4 Microkernel. submitted for publication. Archived from the original on 29 November 2011. Retrieved 10 October 2007.
  34. ^ a b Klein, Gerwin; Elphinstone, Kevin; Heiser, Gernot; Andronick, June; Cock, David; Derrin, Philip; Elkaduwe, Dhammika; Engelhardt, Kai; Kolanski, Rafal; Norrish, Michael; Sewell, Thomas; Tuch, Harvey; Winwood, Simon (October 2009). seL4: Formal verification of an OS kernel (PDF). 22nd ACM Symposium on Operating System Principles. Big Sky, MT, USA.
  35. doi:10.1145/1435458. Archived from the original
    on 24 April 2010. Retrieved 17 August 2009.
  36. ^ a b "TUD Home: Operating Systems: Research: Microkernel & Hypervisor". Faculty of Computer Science. Technische Universität Dresden. 12 August 2010. Archived from the original on 6 April 2012. Retrieved 5 November 2011.
  37. ^ Steinberg, Udo; Kauer, Bernhard (April 2010). NOVA: A Microhypervisor-Based Secure Virtualization Architecture. Eurosys 2010. Paris, France. pp. 209–222. .
  38. .
  39. .
  40. ^ David L. Mills and Poul-Henning Kamp (28 November 2000). "The Nanokernel" (PDF). Retrieved 28 August 2017.

Further reading