Hi George,
[watch mail; I'm p>> E.g., "23" interpreted as a file descriptor in task (process) A canBut there is no *requirement* that this be the case! I.e., if you printed the value of a file descriptor in three instances of an app running on the same machine -- along with three instances under some other OS -- you could expect up to 6 different "local names" for the same physical object.
By contrast, under Amoeba, 0xDEADBEEF12345678 pertains to exactly
*one* object regardless of *who* has that value present in their local memory (as a capability_t). The identity of theobject (along with the allowed permissions) is encoded *in* that value."23" has no such encoding (as a file descriptor *or*, in my case, a Handle)
I.e., Amoeba tasks can pass capabilities to each other via any number of communication methods WITHOUT THE CONSENT OR INVOLVEMENT OF THE KERNEL!
So, for example, a task can display a hex representation of a capability ON A CRT. A human can read that value and type it into another terminal. The task attached to *that* terminal can then store this in memory and present it AS A VALID CREDENTIAL with exactly the same permissions as the original!
No record of this transfer exists.
In Mach, all that first task could do is display the equivalent of a file descriptor to the user. If the user typed that into another process, it would be meaningless, there. You have to tell the kernel to transfer the capability (or a copy of it). It's not just "data".
But not invalidated if possessed by another. I.e., I can send copies of a capability to 100 other tasks. They have exactly the permissions that I had. The kernel is unaware that I have made these transfers -- it's just data! (that was the goal of Amoeba -- to get the kernel out of that business, contrast Mach).
Because the kernel is NOT involved in the duplication of existing capabilities (same permissions), it never knows how many such outstanding references exist.
Yes, as can mine. Same limitations -- the kernel is involved in creating *new* capabilities (different permissions on same object or different objects entirely). But, Amoeba allows exact duplicates to be made "for free". Mach doesn't. The kernel is involved in all Handle (port) operations. Even literal copies. So, it knows how many copies of each Handle exist at any given time (fudging the truth here in the distributed case).
For example, Mach had a "send-once" right. It allowed the holder to send *one* message to a port. It could not be copied by the holder (had to be created by the holder of the corresponding *receive* right) even though holders of regular send rights could ask the kernel to copy (or move/transfer) them.
So, a task could be given 38 send rights for a particular port.
38 messages later, the port stops working.In Amoeba, once you know the ticket's "binary value", you can use it as many times as you want (unless the server backing it has imposed some limitation on its use -- which a mach server could also layer atop any send right!).
I can *move* that send-once right to another task of my chosing thereby giving it the "permissions" that were tied to it (by its server). But, in doing so, *I* lose the ability to use it! In Amoeba, we would *both* retain the capability to use it!
Yes. My point was it allows a name to be revised without being "changed". The title of the book remains the same though its content/role has been altered.
Last year's pass for the park doesn't work this year.
But the reference is the same regardless of who "uses" it! If you give me a binary copy of a ticket, the kernel doesn't know that I now possess that capability (object, permission).
I can freely create a "constrained" capability from this and hand it to someone else. Who, in turn, can *duplicate* that and hand it to yet another.
The only way for any of us to know a ticket (capability) has been revoked is to try to *use* it. If I use mine or you use yours, we get told THAT capability has been revoked. But these other (nameless) two guys in my example have no idea that this has happened (unless one of us knows to tell BOTH of them).
Yes. But literal duplicates of a ticket can be made "for free".
Correct. Ditto Mach.
Correct, also ditto Mach.
In mach, deleting the port (Handle) deletes all instances of it. And, since the kernel knows where those instances are, those Holders can be notified when that happens.
It needs to know the current version of each capability that it will honor. Because actors can present stale tickets.
In Mach, the Handle is *gone*. The Handler never sees it again. Nothing to "remember".
This is the Mach approach -- a Handle is a Handle (a port is a port). The only version that exists is the current version. If I don't want you to have a Handle, I delete that Handle -- there's no way you can reconstruct it "from memory".
The way the "permissions/authorizations" associated with a particular handle are interpreted can change -- without the consent of the Holders (also true in Amoeba). E.g., if he File Server is flushing all buffers to disk because of an impending shutdown, it can choose to ignore the "write" permissions that had previously been granted in EXISTING Handles.
If it *wants* to notify each Holder of this, it can (because it knows which tasks are holding those Handles *now* -- no duplicates without the kernel's consent).
Yes! This is the single biggest drawback! But, it is the only way to enforce communciation restrictions, etc.
E.g., in Amoeba, even bad tickets have to be processed by the backing server. On *that* server's dime (timeslice/resources). In Mach, bad Handles (no send rights) are caught at the local server on the offending actor's dime!
If a Handler sees an actor abusing his privilege (whatever that means), he can revoke the Handle (because the Handler has the receive right for the port!) and shut the actor up *at* the actor's kernel.
In Amoeba, the server is forced to validate (invalidate) each request as it comes in -- regardless of how often, etc. And, tie up RPC/IPC resources as well!
In Amoeba's case, RPC could be reasonably cheap because Amoeba expected processor *pools* -- lots of SBC's in a big box. You could afford fat pipes between boards, shared memory cards, etc. regardless of whether you created them initially. Mach could also support SMP & NUMA multiprocessors but handles NORMA by proxy. Invoking the communications for RPC is expensive. If you can determine that the connection shouldn't be attempted and inhibit it locally, you gain a lot.
(Imagine all the network traffic if a roue/corrupt/adversarial actor kept issuing RPC's to an Amoeba server that was just going to repeatedly say NO_PERMISSION!)
[I am obsessed with DoS attacks as I see this as the achilles heel that will always remain vulnerable in interconnected systems. Witness the extremes I taken in the design of my router/switch! :< Don't even *make* a coonection if its not supposed to exist!]It's just an IPC! Ameoba won't let tasks issue IPC's without a capability?? Who validates *those* (i.e., acts as handler/server for IPC)?
It's still an IPC. Something on the other end has to validate on *its* dime. In my case, that's the kernel EXECUTING IN YOUR TIMESLOT. In Amoeba's case, the kernel has no role in IPC other than to pass the data along to the service encoded in the ticket -- wherever it may currently reside. That service has the only and final word on whether it wants to honor the ticket (at this time).
Am I missing some *new* addition to Amoeba that allows the local kernel to act as selective gatekeeper?
Correct. It also has to track the current state of each executing task, current memory allocations, runtime priorities, etc. I consider communication "rights" to be a key aspect of providing integrity to applications.
Think about it. How can an actor take down my system? It has the final say in only those objects that it backs. In all other cases, it is subservient to other on whom it depends. You can't abuse a service unless the service chooses to let you. If you become abusive, the service can revoke your ability to even *talk* to it!
It amounts to the same thing. They chased after UN*X becaue it would give them a "real application" to demonstrate. Hardly anyone would be interested in watching an "inverted pendulum" controller implemented on yet-another-OS.
So, much of the work went into supporting mechanisms that had no use outside of the UN*X context. E.g., the "environment server". And, many hooks were created just for these things! (e.g., the list of special ports provided to newly created tasks should only have been a *single* port from which all others could be derived).
The "BSDSS" (Single Server). Ditto POE (DOS single server). Then MachUX and eventually MachUS (or perhaps I've got the multiserver mnemonic backwards). Along with the RTMach and Mach4 branches. (I believe I probably have the largest "private" Mach archive in existence. I'm sure many of the older releases are no longer available on *any* public servers!)
NeXT followed this route with 2.5
But even the first "UX" server had too much of a performance hit precisely because of the "kernel congestion" you mentioned above. That's the mistake with pursuing a UN*X goal -- even temporarily. The mechanisms don't fit the application as well as existing UN*X "monolithic kernels" did.
You're gaining something (additional isolation, multiprocessing support, distributed processing, user-level services, etc.) and there's obviously going to be a cost. UN*X itself didn't have (nor apparently
*want* those features so it could be more efficient.The RTMach crew just built on these same assumptions -- instead of saying, "UNIX is great -- for the problems that UNIX solves THE WAY UNIX SOLVES THEM. We're going after a different class of problems and a different methodology for solving them.
Exactly. But, the Mach API *under* another layer of abstraction just makes things worse, not better. Also, there were some problems with the possibility of cross contamination *within* the library -- no doubt another sacrifice to efficiency.
I think the Mach (and Amoeba) focus on application to existing usage paradigms is the fatal flaw. E.g., applications there are transient and invoked by a multitude of users on a multitude of objects. BIG objects.
OTOH, appying those mechanisms to a *control* system where:
- connections tend to persist for a LONG period of time (24/7/365)
- isolation means real dollars (task A doesn't end up scrapping all the product produced by task B)
- processors can have multiple cores
- as well as being physically distributed
- and compute costs decrease considerably faster than memory or communication costs THEN, burning cycles in a kernel (cached) to save references to memory (local or remote) AND unwanted communication costs can have a big payoff.
But, I think you have to approach the OS with these things in mind and not "files" and TTYs, etc. Do you *really* need all these traps? All these function arguments? etc.
At the opposite extreme, see how things like L4 struggle to "run with the big boys". Yah, you can be lean and efficient and fast -- but, what does it cost to implement expensive feature with a few primitives? And, what level of skill to do so with the same sort of efficiency? ("Wow! Kernel traps are twice as fast!" "Yeah, but you need twice as many...")