From my limited perspective Mach and QNX are alike.
With early IBM 360 systems, to which security came late, disk space was allocated coarsely; by cylinder. If application code corrupted the workings of these access modules, only the file to which the application had access was corrupted. RAM (core then) was accessed by real addresses and protected from writing by a 4 bit storage key per page. This scheme prevented IBM from caching files in RAM for the caching logic inhabited user space. VSAM (circa 1975?) solved this somehow.
Mach provides an address space for file logic that is separate from the kernel and also separate from file users.
I understand that the Mach kernel provides separate address spaces for each of several kernel functions. I presume that each such function has its own mutable RAM in which it encodes the abstracted state of its clients. This means that if one client discovers a bug by which he can insert his own code into the file service address space, he can then corrupt file service for all of the file system clients.
By contrast Keykos has segment keepers and directories, one address space per instance. Such an exploit can effect only that instance for the instances are isolated from each other. The exo-kernel solves this problem but fails to fully abstract file mechanisms from the user.
The appendix lists these abstractions produced by the Mach kernel: task, thread, port, port set, message, and memory object. I will contrast these to the Keykos kernel abstractions: page, node, segment, domain, meter, message, and an ever so ephemeral process. (In Keykos the process might be called a fiction with which the kernel’s domain scheduling is explained.) Terminologically only the message is in common between these sets of names.
In Mach a message contains “data objects” and in Keykos messages contain byte strings and a word. Not much difference here but the data objects may optionally be “pointers to out-of-line data” which sounds like some sort of capability. We shall see.
The appendix uses “port right” much as Keykos uses “key”. These can inhabit messages in both systems and thus be passed about. A port is “the basic object-reference mechanism”, much like the key. “messages are queued at the destination port if no thread is immediately ready to receive them.” whereas in Keykos messages are not sent until the recipient is ready to receive it—the sender is blocked meanwhile.
The task includes a virtual memory, some ports rights and may have some threads. The thread is the basic unit of execution and is attached to some specific task. This arrangement is rather like several domains sharing one segment as their address space except that domains have their own keys where the port rights are within the task. I see no fundamental difference here.
A message queue is part of a port set and a message sent to any such port conceptually goes into that queue. The queue is bounded. Messages move from queues to threads perhaps by threads holding some sort of capability to the queue (port set). (I think there is logic to avoid double copying when possible.)
The memory object can be mapped into the address space of a task. It can be managed by “a user-mode external memory manager”—sounds like the Keykos segment keeper.
The appendix goes on to describe how large data transfers are done virtually by memory map magic. I can see that this is possible when some guidelines are followed, such as page alignment.
In Keykos we would advise passing a segment key in such cases. This is somewhat more conceptual work for user code in that the source and sink of such passed data must occupy pre-planned addresses in the respective spaces. Page tables for the passed segment remain intact and shared in this case. The semantics are different—sometimes better and sometimes worse. Keykos was spoiled a bit having originated on a machine (370) with flexible and inordinately fast memory to memory transfer operations.
I see no Mach kernel hooks to administer scheduling policy like Keykos meters.
It seems that “an exception handler is just another thread in the task in which the exception occurs.”. This is good in that one does not interrupt threads, it is bad in that the program that handles the exception has the same authority as the program that suffered the exception.
I don’t see that port receive rights can be passed about. This would mean that a port set is permanently bound to a task.