By means of the layered technique, the designers have an option where to draw the kernel-user boundary. Usually, all the layers went in the kernel, but that is not necessary. In reality, a strong case can be made for putting as little as possible in kernel mode because bugs in the kernel can bring down the system instantly. On the contrary, user processes can be set up to have less power so that a bug there may not be lethal.
Many researchers have studied the number of bugs per 1000 lines of code (e.g., Basilli and Perricone, 1984; and Ostrand and Weyuker, 2002). Bug density depends on module size, module age, and more, but a ballpark figure for serious industrial systems is ten bugs per thousand lines of code. This means that a monolithic operating system of five million lines of code is likely to contain something like 50,000 kernel bugs. Not all of these are fatal, certainly, since some bugs may be things like issuing an incorrect error message in a situation that seldom occurs. However, operating systems are adequately buggy that computer manufacturers put reset buttons on them (often on the front panel), something the manufacturers of TV sets, stereos, and cars do not do, in spite of the large amount of software in these devices.
The main idea behind the microkernel design is to attain high reliability by splitting the operating system up into small, well-defined modules, only one of which-the microkernel-runs in kernel mode and the rest run as relatively powerless normal user processes. Particularly, by running each device driver and file system as a separate user process, a bug in one of these can crash that component, but cannot crash the whole system. Thus a bug in the audio driver will cause the sound to be garbled or stop, but will not crash the computer. On the contrary, in a monolithic system with all the drivers in the kernel, a buggy audio driver can easily reference an invalid memory address and bring the system to a grinding halt immediately.
Many microkernels have been implemented and deployed (Accetta et al., 1986; Haertig et al., 1997; Heiser et al., 2006; Herder et al., 2006; Hildebrand, 1992; Kirsch et al., 2005; Liedtke, 1993, 1995, 1996; Pike et al., 1992; and Zuberi et al., 1999). They are especially common in real- time, industrial, avionics, and military applications that are mission critical and have very high reliability requirements. A few of the better-known microkernels are Integrity, K42, L4, PikeOS, QNX, Symbian, and MINIX 3 . We will now give a brief overview of MINIX 3, which has taken the idea of modularity to the limit, breaking most of the operating system up into a number of independent user-mode processes. MINIX 3 is a POSIX conformant, open-source system freely available at www.minix3.org (Herder et al., 2006a; Herder et al., 2006b ).
The MINIX 3 microkernel is only about 3200 lines of C and 800 lines of assembler for very low-level functions such as catching interrupts and switching processes. The C code manages and schedules processes, handles interprocess communication (by passing messages between processes), and offers a set of about 35 kernel calls to allow the rest of the operating system to do its work. These calls carry out functions like hooking handlers to interrupts, moving data between address spaces, and installing new memory maps for newly created processes. The process structure of MINIX 3 is shown in the following figure, with the kernel call handlers labeled Sys. The device driver for the clock is also in the kernel because the scheduler interacts closely with it. All the other device drivers run as separate user processes.