You would be right about the flexibility of loadable kernel modules if it weren't for the fact that a loadable kernel module can easily bring down the entire system. I'd agree with the "services can easily form complex dependency chains and therefore aren't much different from a monolithic kernel" statement if the industry hadn't shown us ways of dealing with that already. I'm mainly talking about a failover strategy. A modern computer could trivially handle it. Imagine that you need to update the disk service so it can handle a new disk standard. You have a few ways of going about this:vvaltchev wrote:I won't even try Microkernels are what the mainstream opinion (professors, researchers, most developers here etc.) considers better, so go with it.AndrewAPrice wrote: Change my mind
I can share my opinion though, hoping to not end up in an endless (and hatred) discussion. I really don't have time for that, guys.
I completely agree with Linus Torvalds' on the topic: despite the dislike of many people, the industry has proven him right. Most of the so-called "microkernel" real-world operating systems are not truly microkernel and avoid message passing & context switching at all cost. The overhead of message-passing is excessive and a well-written monolithic kernel can always outperform a complex micro-kernel OS both in terms of latency and throughput. Even if in a microkernel architecture the kernel itself will be simpler, the overall complexity will grow significantly compared to a monolithic implementation. In particular, if you try to care about performance and start doing all sort of "tricks" to speed up the insanely-slow native implementation. The increased stability of microkernels in case a module (service) crashes, that is so-much praised by Prof. Tanenbaum, is an overrated IMHO: you cannot really get the work done on a microkernel even if a small part of it crashes and gets restarted: that would cause an insane amount of side-effect failures on userspace. Therefore, the whole OS has to be extremely well tested and work flawlessly, no matter the architecture. Partial or total crashes cannot be realistically tolerated. Therefore, if that's the case, why even bothering with a complex microkernel system? If everything needs to be close to perfect and work flawlessly, why not implementing it in the simplest way possible? Also, with the addition of loadable modules and user-space filesystems (see FUSE), a "monolithic" kernel like Linux can be fairly flexible.
- Make the service modular using an embeddable scripting language like JS or Lua.
- Use a failover instance while you update the master instance.
The first option (using scripting languages) is also another good idea. FreeBSD does this in the kernel, as has been mentioned previously, but I don't think that much of the kernel even uses it right now (I might be wrong about that one). That would be significantly easier to do in user mode, though, since you wouldn't need to do much modification (if you had to do any at all) for it to work properly, whereas integrating that kind of thing into the kernel requires actually modifying the source code or writing your own implementation. I think that the overhead of message passing has been somewhat, if not entirely, mitigated due to the advent of things like COW and, of course, the ability to share pages (mapping the same page in multiple address spaces simultaneously, such that reads and writes from both ends are reflected in all processes where its mapped) (I like to call this "page mirroring"). Your "message passing" then boils down to notifying the target process that a message is available for reading. And if you use raw binary representation, and don't bother with "packaging" it up into some weird format, that's one less thing you have to worry about. Of course, locking is an issue, and you'll have to figure out how to signal processes when they can and can't write/read the data, but IPC synchronization mechanisms are not a new concept and exist already. Just my thoughts.