In QNX, everything is a message, including files. The basic primitive is MsgSend, sent to another process which has a MsgRecv outstanding. The other process sends a reply back with a MsgReply, which unblocks the MsgSend and returns data. Any amount of data can be sent. POSIX file primitives, open/close/read/write, are small functions that make MsgSend calls.
MsgSend is a more useful primitive than a file. It's a subroutine call across processes, a better fit to anything that isn't stream I/O. In most OSs, you want a subroutine call, but the OS gives you an I/O operation. Linux has to stand on its head for operations such as getting all the properties of a pluggable device, because returning a variable length array of fixed-format records as an atomic operation isn't a Linux primitive.
Microkernels have a bad reputation because interprocess communication in Mach was botched. Mach was built on BSD. To do this right, you have to design CPU scheduling and message passing together, and they need to be tightly coupled. The key to interprocess communication is arranging the normal path for a message pass to throw control from one process to another without a trip through the scheduler. Get that wrong and your microkernel will be sluggish.
sendmsg isn't a synchronous operation, and there is no lock-step operation of sender and receiver supplying and retrieving a result. Sure you can make something QNX-like, but parent's point was much more general than that: most OS APIs and primitives are not easily expressed in terms of bytestreams
Yes, and when you express them as bytestreams, you end up with framing and marshalling and serialization. People start sending JSON. Then they want to put everything in one process because the performance is so low.
Are they symmetrical philosophies? As a layman, this appears to mirror the financial debate between everything being stocks (“the quantum of an economy is a dollar of assets!”) and everything being flows (“the quantum is a transaction!”), or physics’ particle-versus-wave formulations.
You've just pushed me into 20 Minutes of silent pondering and arguing with myself. My girlfriend asked me if something bad happened because I was just staring wordlessly into the air.
Of course "vi" has all its bases covered, by alternating between the cursor being between two characters in "insert" mode, and over one character in "normal" mode:
>In insert mode, the cursor is between characters, or before the first or after the last character. In normal mode, the cursor is over a character (newlines are not characters for this purpose). This is somewhat unusual: most editors always put the cursor between characters, and have most commands act on the character after (not, strictly speaking, under) the cursor. This is perhaps partly due to the fact that before GUIs, text terminals always showed the cursor on a character (underline or block, perhaps blinking). This abstraction fails in insert mode because that requires one more position (posts vs fences).
>Switching between modes has to move the cursor by a half-character, so to speak. The i command moves left, to put the cursor before the character it was over. The a command moves right. Going out of insert mode (by pressing Esc) moves the cursor left if possible (if it's at the beginning of the line, it's moved right instead).
>I suppose the Esc behavior sort of makes sense. Often, you're typing at the end of the line, and there Esc can only go left. So the general behavior is the most common behavior.
>Think of the character under the cursor as the last interesting character, and of the insert command as a. You can repeat a Esc without moving the cursor, except that you'll be bumped one position right if you start at the beginning of a non-empty line.
>Make VIM normal-mode cursor sit between characters instead of on them
>I would really like it if the VIM cursor in normal mode could act like it does in insert mode: a line between two characters. So for example:
>- Typing vd would have no effect because nothing was selected
>- p and P would be the same
>- i and a would be the same
>Has anything like this been done? I haven't been able to find it.
>Answers:
>The idea that the cursor is always on a line and on a character position or column is inherent in Vim's design. If you were to try to change that, many of Vim's operations would behave differently or would not work at all. It's not a good idea. My advice would be that you learn and become accustomed to Vim's basic behavior and not try to make it behave like some other editor. – garyjohn Feb 5 '11 at 23:55
>What you want is not Vim, I'm afraid. – romainl Feb 6 '11 at 7:15
Not sure what API approach they use, but in the area of microkernels:
- GenodeOS is reportedly used in production by some commercial clients, though mostly undisclosed IIRC; it is a higher-level layer (~OS) compatible with numerous microkernels
- Fuchsia OS - in dev; it's a recent development by Google; as much as Google is officially silent about it (I understand they're not sure how the experiment will work out for them), observers assume it's most probably hoped to be used as a successor to Android
- Redox OS - in dev; no concrete news of mainstream usage plans I know of, but has some mindshare among developers
- Minix 3 - used in production; infamously by Intel in their IME
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.
It's a low level RPC mechanism, it does reschedule to the new process immediately without a trip through the scheduler when it's used, it has similar blocking semantics, etc.
It's also worth following the "bus1" work for linux which may end up being quite similar as well.
QNX is mainstream as hell, just not anywhere you'd expect there to be an OS. It's an RTOS and gets used in automotive, medical, and network technologies where software latency needs to be tightly controlled. You wouldn't use it in the same place you'd use Linux anyway.
This is true. I suppose I'm just curious about more general (server/desktop/mobile) use cases, because the IPC and scheduling issues the parent mentions must be extremely difficult to predict with a wide array of workloads.
It's really not designed for those use cases though. We were using it for a GUI and or finding was that without constraining the resource usage of the GUI using Adaptive Partitioning user-driven workloads could stomp all over the lower-priority processes. This effectively meant that a poorly-configured system would eventually trip the watchdog due to user input.
QNX does include features to control this but you have to know enough about it to realize you want them. And POSIX priorities are not enough to prevent this from happening because they are solving a different problem.
You might find it useful in servers where controllable latency is more important than responsiveness, but I wouldn't use it in a user-driven workload like a desktop OS without very carefully configuring it. Out-of-the box it can be very unstable.
I remember using the single disk QNX demo back in the day and being highly impressed with the functionality the crammed into that disk. Sad that it never went anywhere as a desktop/server OS.
I ran QNX as a desktop OS for three years when I was working on a DARPA Grand Challenge vehicle. The vehicle ran QNX, so the development systems did, too. It had an early version of Firefox, the Eclipse IDE, Thunderbird mail, and all the Gnu command line tools. Sadly, QNX under Blackberry no longer offers the desktop environment. And QNX's open source/closed source/open source/closed source transitions angered the development community so much that people stopped offering QNX versions of UNIX/Linux software.
The most striking thing about running QNX on the desktop was the absolutely consistent response. It doesn't page, so everything is in memory. Going back to Linux felt so laggy.
You pay about a 10%-20% overhead cost for all that interprocess communication. It's not a big deal on modern processors, because when you send a message, the receiving process is running on the same CPU and the data is usually in L1 cache since the sender just put it there. So copying is cheap.
Incidentally, all the Boston Dynamics robots run QNX. They're coordinating all those limbs and valves from one CPU. It's not distributed; that would be too uncoordinated. Valve update is at 1KHz; balance update is at 100Hz.
> And QNX's open source/closed source/open source/closed source transitions angered the development community so much that people stopped offering QNX versions of UNIX/Linux software.
Was it ever really open? I can't find copies of any version (regardless of how old)
Well the importance of the kernel on application performance is often over-estimated, especially for non-server software.
xnu is a hybrid of Mach and FreeBSD. To be super-concrete, it has both sets of syscalls, with Mach syscalls as negative integers, and BSD syscalls as positive integers. A given running executable has a dual existence as both a BSD pid_t and a Mach task_t. At one point it was even possible to have a Mach task without a pid.
Most executed syscalls are BSD: read, write, select, getpid, etc. However Mach still plays a role in the VM system (`vm_copy`, etc.) and crucially via the IPC workhorse `mach_msg`.
It's hard to overstate the importance of mach_msg. It underlies the great macOS IPC primitives: xpc, notify(3), etc. Linux looks anemic by comparison with its limited pipes, creaky SysV sendmsg(), and DBus (hope you never have to use it). But it also appears that Apple is preparing to drop Mach: mach_msg / MIG is buried, xpc is conspicuously designed to be separable from Mach, etc.
They pulled some of the low-level I/O drivers into the kernel because the IPC overhead was too great. They call it a "hybrid" kernel rather than a pure microkernel.
To my understanding (please correct if wrong!) it's not only drivers, it's most of a BSD-style kernel that XNU runs in the same memory space as Mach. I'm not sure what difference that makes in practice versus the more conventional BSD kernels — I think a lot of IPC happens through Mach ports? … and I think Mach shows in the realms of signal delivery, what resources are waited on with `select`, etc.
MsgSend is a more useful primitive than a file. It's a subroutine call across processes, a better fit to anything that isn't stream I/O. In most OSs, you want a subroutine call, but the OS gives you an I/O operation. Linux has to stand on its head for operations such as getting all the properties of a pluggable device, because returning a variable length array of fixed-format records as an atomic operation isn't a Linux primitive.
Microkernels have a bad reputation because interprocess communication in Mach was botched. Mach was built on BSD. To do this right, you have to design CPU scheduling and message passing together, and they need to be tightly coupled. The key to interprocess communication is arranging the normal path for a message pass to throw control from one process to another without a trip through the scheduler. Get that wrong and your microkernel will be sluggish.