In 1984, Richard Stallman founded the GNU Project with the goal of creating a completely Free Software UNIX-like operating system. By the late ’80s, the project had more or less succeeded. Only one major component was missing: a kernel. A kernel is both the most and least important part of the system. The most important, because without it the computer can’t run any of the other programs. The least important, because as long as a kernel implements the POSIX interfaces then it’s more or less interchangeable with any other kernel that does so.
In 1987, the GNU Project began looking for a kernel. One option was 4.2 BSD, with the remaining AT&T code rewritten. Another was a new research kernel being developed at CMU, called Mach.
Since those days, HURD has acquired the same reputation in operating system circles that Duke Nukem Forever has among gamers.
The Speed of Sound
The Mach kernel was very different from the BSD kernel. I’ve discussed a few BSD kernels in this series of articles, and they all have one thing in common: They’re very boring. This isn’t necessarily a criticism. The main task for a kernel is to stay out of the way of users, and a boring kernel does this very well. If you’re planning on writing code for a kernel, however, a boring kernel just isn’t as much fun.
Mach was a very exciting kernel. It was one of the first attempts at a new idea in kernel design: the microkernel. The microkernel took the UNIX philosophy of small tools doing one thing well, and applied that principle to the kernel. A microkernel should provide process isolation well. Everything else should be handled by other processes. Drivers should be in their own process, with limited access to the hardware; each filesystem should be in its own process; and so on.
A few operating systems have been written based on Mach, with OS X being the most popular. All of these systems use what’s known as the "single server" approach. The microkernel provides some basic hardware abstraction, and all of the other operating system services are provided by a single process, typically a modified version of BSD. Porting is relatively easy, because only the small Mach kernel is hardware-specific. This approach also makes it possible to run two or more complete BSD-like environments side by side.
Otherwise, however, there aren’t many advantages to the "single server" approach, and there are some disadvantages. Mach generally is considered to be one of the major reasons that microkernels are not popular today. The problem is the way in which system calls are handled on BSD-like versus Mach-like kernels. A system call is the way in which a typical userspace process interacts with the system. Any time you want to do something that a userspace process can’t do for itself—allocating more memory; reading from or writing to a device such as the screen, keyboard, or disk; and so on—you do it via a system call.
In a BSD-like kernel, you push the arguments onto the stack as you would for a normal function call. Then, rather than issuing a call (or jump) instruction, you write the number of a system call to a specific register and issue an interrupt. The kernel’s interrupt handler reads the value and calls the correct function, which then performs a privileged operation. On some architectures, including modern x86 variants, a special system call instruction is used in place of the interrupt.
In the Mach kernel, most things that were typically implemented as system calls in a BSD kernel are performed by sending Mach messages to a specific process that handles them. Mach messages are roughly an order of magnitude more expensive than BSD system calls. A large part of this expense comes from the fact that the microkernel performs access-right checking on every message, which is very expensive. More modern microkernels, such as L4 and Xen, don’t do this. Xen doesn’t even implement the message passing in the microkernel; it just provides a mechanism for sharing memory, and allows processes (virtual machines) to use ring buffers in these for message passing.