Ethin wrote:
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.
Sure, agreed at 100%. My point was that if the microkernel as a whole (including the microservices) was unreliable (e.g. up and working 99.9% of time means unreliable to me), it's still unacceptable to have even intermittent failures, the same way as a module causing the whole system to crash. The tolerance for bugs is very little. In other words, complex systems that partially fail but without fully crashing are to me generally worse than systems which either work or crash. I'm all for very intensive testing, instead of "exception-driven" systems, where errors happens all the time almost as a normal routine.
If you write all your code in a completely unsafe language like C and a small bug could make the whole kernel to crash, but you have 100% line coverage and a ton of tests like unit tests, system-tests, stress-tests plus static analysis etc. why would you care? I prefer having powerful and exhaustive tests than fewer tests and claiming that my code is "safe". My assumption is that kernel code must not crash. Under that assumption and world-view the services in a microkernel arch. are mostly overhead.
Ethin wrote:
- Make the service modular using an embeddable scripting language like JS or Lua.
- Use a failover instance while you update the master instance.
I'm very against using managed languages for kernel code
The "limitation" of native languages is not a problem to me.
Anyway, I fully recognize the advantage of failover strategy you mentioned. It's clearly more flexible than what a monolithic kernel can typically do. It's worth probably saying that, theoretically, something like this could be implemented with modules as well. It will require supporting two loaded modules for the same thing plus an atomic switch from one to the other. But, yeah, typically monolithic kernels don't support fancy features like that.
Ethin wrote:
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.
Those are the tricks I was mentioning. Here are the drawbacks:
1. no matter how much memory sharing you do, you cannot avoid the extra context switches. And they are expensive, if they happen often. And here there is, again, a whole set of smart optimizations that could be done, nothing of which completely eliminates the problem, but increases the complexity. In the monolithic kernel we would have just used simple function calls instead of complex async queue systems.
2. the more memory sharing you do, the further you go from the "true" microkernel architecture with message passing. Also, you increase the likelihood of a crash affecting multiple services and so the whole system. So, memory sharing is an
unsafe technique that at least partially breaks the microkernel architecture.
3. If you start using managed languages like Lua, you'd really have to pay the whole price of serialization and de-serialization of binary data. Memory sharing goes off the table. Also, the increased latency of such languages is significant compared to native languages. I wouldn't use a kernel that's slower than what it could be. But, forget me: in the large scale (the cloud) the economics would be in favor of the more efficient solution. Think about how much it would cost to add a +30% overhead to 10 million machines. +30% is not a random number. It is, to my best knowledge, the typical overhead a good microkernel has over a monolithic one. I believe Prof. Tanenbaum mentioned himself that number. He believes it's totally worth to pay that extra price for the increased stability and flexibility. Many people disagree. If it was something like +1%, there would had been no discussion probably.