The Nintendo Switch's Kernel, Horizon/NX , is an example of a microkernel, tailored for their specific use-case, and is in wide use[0]. It is, sadly, closed source, however it has been reverse engineered. Their IPC API is, I believe, pretty smart and elegant:
- There is a per-thread IPC zone of 0x100 bytes. When doing IPC, the request is serialized and put into this. If bigger data than 0x100 bytes is necessary, pointers are passed around, and the Kernel maps it into the process servicing the call.
- svcSendSyncRequest is used to call an IPC. It is a synchronous API that will block until the process servicing the call replies to it.
- svcReplyAndReceive is used to receive an IPC request and reply to a request, and then wait until a new one is received. The syscalls are "merged" into a single one to avoid the syscall overhead: Almost all svcReply will be followed by an svcReceive, so merging them into a single call makes a lot of sense.
You can find more information about the SVCs at [1] and the IPC layout at [2].
A little note about this: Horizon/NX actually traces back to the Horizon OS on the Nintendo 3DS. The IPC marshalling was significantly more simple back then[1]. In all honesty, I'm not sure what made Nintendo thing the IPC marshalling on the NX was a good idea; to me it just looks like a hastily-designed mess (cf. "This one is packed even worse than A, they inserted the bit38-36 of the address on top of the counter field." on the [2] page linked by the parent comment). However, the NX incarnation was designed with "naturally" wrapping C++ methods in mind and it does a fairly decent job at that.
- There is a per-thread IPC zone of 0x100 bytes. When doing IPC, the request is serialized and put into this. If bigger data than 0x100 bytes is necessary, pointers are passed around, and the Kernel maps it into the process servicing the call. - svcSendSyncRequest is used to call an IPC. It is a synchronous API that will block until the process servicing the call replies to it. - svcReplyAndReceive is used to receive an IPC request and reply to a request, and then wait until a new one is received. The syscalls are "merged" into a single one to avoid the syscall overhead: Almost all svcReply will be followed by an svcReceive, so merging them into a single call makes a lot of sense.
You can find more information about the SVCs at [1] and the IPC layout at [2].
[0] Preempting the "I thought the switch used BSD": wikipedia was wrong, the OS is completely custom, and the kernel is tailor-made. [1] http://switchbrew.org/index.php?title=SVC [2] http://switchbrew.org/index.php?title=IPC_Marshalling