3 ms·
In a monolithic kernel, the kernel is essentially one giant blob of code in a single address space. A userland process calls into the kernel via a syscall CPU i
by mwfunk 10y ago
In a monolithic kernel, the kernel is essentially one giant blob of code in a single address space. A userland process calls into the kernel via a syscall CPU instruction, which is sort of like a function call that also enables transitioning from userland to kernel space in a controlled and (potentially) secure way. This is very efficient.
Modern OSes run each process in its own address space via CPU-level support for virtual memory management. Older (or simpler) OSes which don't use virtual memory simply had one big address space that all processes shared. This was Windows, pre-Windows 95, and MacOS, pre-OS X. You word processor could crash and not only take down other applications (since they were all in the same address space), but could also take down (for example) the graphics driver, because the system software was also in this one unprotected address space.
This type of older system is much less robust, because the stability of the entire system is effectively equal to the stability of the least reliable program being run on it. On modern OSes with monolithic kernels, process separation, and preemptive multitasking, an unreliable program simply crashes without affecting anything else on the system.
In a sense though, the monolithic kernel still has the same problem that userland processes had before. It's only as stable (and secure) as the least stable driver or kernel component. If anything goes wrong at the kernel level, it could easily take down the entire kernel. There's nothing to prevent a misbehaving graphics driver (for example) from overwriting memory being used by other drivers or the base kernel itself. Likewise, any exploit that can inject code into a driver/kernel module/whatever then has complete access to everything on the system.
Microkernels close the loop by chopping up the kernel itself into separate processes. The heart of the kernel is still a (much, much smaller) blob of code providing virtual memory management and a scheduler, but also a very low level and efficient IPC mechanism (efficient relative to other IPC mechanisms, that is). The rest of the kernel is divvied up into their own processes. Device drivers and other sorts of standalone kernel modules built on top of the core microkernel all get moved into their own processes/address spaces, and communicate with each other (and userland) via whatever the core IPC primitive is.
The benefit is that a lousy graphics driver can no longer take down the entire system or be an attack surface for malware taking over the entire system. In theory at least, you could hypothetically restart a misbehaving driver just like you can just relaunch an app if it crashes.
That's awesome, but it comes at a cost. The cost is that userland/kernelspace transitions are no longer plain old syscall instructions. Now they become client/server interactions via the IPC mechanism. Inside the kernel, many things that used to be simple function calls also turn into client/server interactions via IPC.
So, you get something that's an obvious win: bringing kernels into the modern era with hardware-enforced process separation, instead of just doing it for userland processes. But if the performance cost is too high, it's not worth it. The cost/benefit analysis surrounding microkernels so far has generally always favored performance, so microkernels were rarely used since monolithic kernels were "good enough" and didn't have the perf hit.
Take this all with a grain of salt; I'm really only familiar with Mach and Mach-era arguments about the feasibility of microkernels. More modern microkernels like L4 and QNX have presumably solved at least some of these problems by introducing a different set of tradeoffs. But AFAIK the basic concepts are unchanged, and the goals of microkernel architectures are the same, just with different implementation strategies.