article Related Topics:
Microkernel
 

A microkernel is a minimal computer operating system kernel providing only basic operating system services (system calls), while other services (commonly provided by kernels) are provided by user-space programs called servers. Commonly, microkernels provide services such as address space management, thread management, and inter-process communication, but not networking or display for example.

Later extensions of this concept lead to new architectures such as nanokernels, exokernels and hardware abstraction layers.

Kernel bloat

Early operating system kernels were rather small, partly because computer memories were small. As the capability of computers grew, the number of devices the kernel had to control also grew. Early versions of UNIX had kernels of quite modest size, even though those kernels contained device drivers and file system managers. When address spaces increased from 16 to 32 bits, kernel design was no longer cramped by the hardware architecture, and kernels began to grow. (See History of Unix).

Berkeley UNIX (BSD) began the era of big kernels. In addition to operating a basic system consisting of the CPU, disks and printers, BSD started adding additional file systems, a complete TCP/IP networking system, and a number of "virtual" devices that allowed the existing programs to work invisibily over the network.

This growth continued for several decades, resulting in UNIX, Linux, and Microsoft Windows kernels with millions of lines of kernel code. For example Red Hat Linux 7.1 contains about 2.5 million lines of source code in the kernel (of about 30 million in total), while Windows XP is estimated at twice that. See Source lines of code for a table of kernel code sizes for various operating systems.

Inter-process communication

Inter-process communication (IPC) is any mechanism which allows separate processes running on the same operating system to intercommunicate, usually by sending messages. This allows the operating system to be built of a number of small programs called servers, which are used by other programs on the system. Most or all hardware support is handled in this fashion, with programs for networking, file systems, graphics, etc.

There are two basic approaches to IPC -- synchronous and asyncronous. Synchronous communication behaves, to the sender, somewhat like a subroutine call -- a request is made, the caller waits, and when the request has been serviced, the caller regains control. This model behaves, from the caller perspective, much like a kernel call to a monolithic kernel.

In a microkernel system, the synchronous kernel call model must be extended to allow one program to call another. Various microkernels have taken different approaches to the problem.

QNX interprocess communication is synchronous, and consists of sending a message from one process to another and waiting for a reply. This is a single operation, called MsgSend. The message is copied by the kernel from the address space of the sending process to that of the receiving process. If the receiving process is waiting for the message, control of the CPU is transferred at the same time, without a pass through the CPU scheduler. Thus, sending a message to another process and waiting for a reply does not result in "losing one's turn" for the CPU. This tight integration between message passing and CPU scheduling is one of the key mechanisms that makes QNX message passing broadly usable. Most UNIX and Linux interprocess communication mechanisms lack this tight integration, although an implementation of QNX-type messaging for Linux does exist. Mishandling of this subtle issue is a primary reason for the disappointing performance of some other microkernel systems. Minix 3 does have the mechanism for this. (ref. Minix 3 book, p. 183).

With asynchronous messaging, the message sender places data on a queue, the message sender is not blocked but continues to run when sending a message, unless the queue is full. The Berkeley sockets model from the UNIX world, which follows the earlier UNIX byte-stream pipe mechanism, fits this model. POSIX adds asychronous message queues, which queue and send discrite messages. *

It is common to construct synchronous messaging from asynchronous messaging, by sending a message, then waiting for a reply. (See Inter-process communication for examples of such systems.) But this is inefficient and results in scheduling delays, as described above. Microkernels, with their extensive use of interprocess communication, need higher performance. Thus, most microkernel systems (including Mach, QNX, and Minix 3) offer some form of synchronous messaging.

Servers

Microkernel servers are programs like any others, except that the kernel grants some of them privileges to interact with parts of memory that are otherwise off limits to most programs. This allows some servers 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 monolithic UNIX kernel. The necessary servers are started at system startup and provide services, such as file, network, and device access, to ordinary application programs. The functions in the kernel of such a system are thus quite limited. 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 for by simply stopping and restarting the server. In a traditional system, a crash in any of the kernel-resident code would result in the entire machine crashing, forcing a reboot. However, part of the system state is lost with the failing server, and it is generally difficult to continue execution of applications, or even of other servers with a fresh copy. For example, if a server responsible for TCP/IP connections is restarted, applications could be told the connection was "lost" and reconnect to the new instance of the server. For QNX, this capability is offered as the QNX High Availability Toolkit.

In order to make all servers restartable, some microkernels have concentrated on adding various database-like techniques like transactions, replication and checkpointing need to be used between servers in order to preserve essential state across single server restarts. A good example of this is ChorusOS, which was targeted at high-availability applications in the telecommunications world. Chorus included features to allow any "properly written" server to be restarted at any time, with clients using those servers being paused while the server brought itself back into its original state.

Essential components


The minimum set of services required in a microkernel seems to be address space management, thread management, inter-process communication, and timer management. A few operating systems approach this minimum, notably QNX and IBM's VM. Everything else can be done in a user program, although device drivers implemented as user programs may require special privileges to access I/O hardware.

Start up (booting) of a microkernel can be difficult. The kernel alone may not contain enough services to start up the machine Thus, either additional code for startup, such as key device drivers, must be placed in the kernel, or means must be provided to load an appropriate set of service programs during the boot process. For this reason, most microkernels do place some "external" code in the kernel itself, notably key device drivers. LynxOS and the original Minix are examples. Many also include a file system in the kernel, which makes booting easier and improves performance.

A key component of a microkernel is a good IPC system. Since many services are be performed by user programs, good 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.

Some microkernels are designed for high security applications. EROS and KeyKOS are examples. Part of secure system design is to minimize the amount of trusted code; hence, the need for a microkernel. Work in this direction has not resulted in widely deployed systems, with the notable exception of systems for IBM mainframes such as KeyKOS and IBM's VM.

Performance


Traditional performance problems with microkernels revolve around the costs of IPC. The costs are due to the extra work Microkernels do to copy data between servers and application programs, and the extra context switch operations.

Attempts have been made to reduce or eliminate the copying cost by using the memory management unit (MMU) to transfer the ownership of memory pages between processes. This approach, which is used by Mach, adds complexity but reduces the overhead for large data transfers. L4 adds a lightweight mechanism using registers if the amount of data being passed is small, which can dramatically improve performance, both in terms of copying, and avoiding misses in the CPU's cache. By contrast, QNX does all IPC by direct copying, incurring some extra copying costs but reducing code size and complexity.

Systems that support virtual memory and page memory out to disk create additional problems for IPC. Unless both the source and destination areas are currently in memory, copying must be delayed, or staged through kernel-managed memory. Copying through kernel memory adds an extra copy cost and requires extra memory. Delaying copying for paging delays complicates the IPC. QNX avoids this problem by not paging, which is the usual solution for a hard real-time system like QNX.

Reducing context-switch cost requires careful design of the interaction between IPC and CPU scheduling. Historically, UNIX IPC was based on the UNIX pipe mechanism and the Berkeley sockets mechanism used for networking. But neither of these has the performance needed for a usable microkernel. Both are unidirectional I/O-type operations, rather than the subroutine-like call-and-return operations needed for efficient user to server interaction. Mach has very general primitives which tend to be used in a unidirectional manner, resulting in scheduling delays. The Vanguard microkernel supported the "chaining" of messages between servers, which reduced the number of context switches in cases where a message required several servers to handle the request.

The question of where to put device drivers owes more to history than design intent. In mainframes, I/O channels have memory management hardware to control device access to memory, and drivers need not be entirely trusted.* The Michigan Terminal System (MTS), in 1967, had user-space drivers, the first operating system to be architected in that way.

Most minicomputers and microcomputers have not interposed a memory management unit between devices and memory. (Exceptions include the Apollo/Domain workstations of the early 1980s.) Since device drivers thus had the ability to overwrite any area of memory, they were clearly trusted programs, and logically part of the kernel. This led to the traditional driver-in-the-kernel style of UNIX, Linux, and Windows.

As peripheral manufacturers introduced new models, driver proliferation became a headache, with thousands of drivers, each able to crash the kernel, available from hundreds of sources. This unsatisfactory situation is today's mainstream technology.*

With the advent of multiple-device network-like buses such as USB and FireWire, more operating systems are separating the driver for the bus interface device and the drivers for the peripheral devices. The latter are good candidates for moving outside the kernel. So a basic feature of microkernels is becoming part of monolithic kernels.

Security


Recently (2006) debate has increased about the potential security benefits of the microkernel design.

Many attacks on computer systems take advantage of bugs in various pieces of software. For instance, one of the common attacks is the buffer overflow, in which malicious code is "injected" by asking a program to process some data, and then feeding in more data than it stated it would send. If the receiving program does not specifically check the amount of data it received, it is possible that the extra data will be blindly copied into the receiver's memory. This code can then be run under the permissions of the receiver. This sort of bug has been exploited repeatedly, including a number of recent attacks through web browsers.

To see how a microkernel can help address this, first consider the problem of having a buffer overflow bug in a device driver. Device drivers are notoriously buggy, but nevertheless run inside the kernel of a traditional operating system, and therefore have "superuser" access to the entire system. Malicious code exploiting this bug can thus take over the entire system, with no boundaries to its access to resources. For instance, an attack on the networking stack over the internet could then ask the file system to delete everything on the hard drive, and no security check would be applied because the request is coming from inside the kernel. Even if such a check were made, the malicious code could simply copy data directly into the target drivers, as memory is shared among all the modules in the kernel.

A microkernel system is somewhat more resistant to these sorts of attacks for two reasons. For one, an identical bug in a server would allow the attacker to take over only that program, not the entire system. This isolation of "powerful" code into separate servers helps isolate potential intrusions, notably as it allows a the CPU's memory management unit to check for any attempt to copy data between the servers.

But a more important reason for the additional security is that the servers are isolated in smaller code libraries, with well defined interfaces. That means that one can audit the code, as its smaller size makes this easier to do (in theory) than if the same code was simply one module in a much larger system. This doesn't mean that the code is any more secure, per se, but that it should contain less bugs in general. This not only makes the system more secure, but more stable as well.

Key to the argument is the fact that a microkernel "automatically" isolates high-privilege code in protected memory because they run in separate servers. This isolation could likely be applied to a traditional kernel as well. However, it is precisely this mechanism that forces data to be copied between programs, leading to the microkernel's generally slower performance. In the past, outright performance was the main concern of most programs. Today this is no longer quite as powerful an argument as it once was, as security problems become endemic in a well-connected world.

But securing the kernel by no means guarantees system security. For instance, if a bug remained in the system's web browser that allowed attack, that attack could still legally ask the file system to erase the drives via the normal IPC messages. Securing against these sorts of "reasonable requests" is considerably more difficult unless a very complex system of rights is available. Even with this capability, the complexity of the interconnections between various programs in the system makes it difficult to apply security checks that are themselves free of bugs.

Examples


Examples of microkernels and operating systems based on microkernels:

Articles


  • - the basic QNX reference.

  • -the basic reliable reference.

  • - the basic Mach reference.

References


See also


External links


Operating system technology | Operating system kernel

Mikrojádro | Microkernel | Microkernel | Micronoyau | Mikrokernel | Микроядро | Mikrojądro

 

This article is licensed under the GNU Free Documentation License. It uses material from the "Microkernel".

Home Pageartsbusinesscomputersgameshealthhospitalshomekids & teensnewsphysiciansrecreationreferenceregionalscienceshoppingsocietysportsworld