The other day, I had an idea. This conclusively proves that people smarter than me already thought of this decades ago. What I'm wondering is... Is this a good idea? Was it ever implemented in commercial hardware? Did it work out? (Presumably not...)
Pretty much every modern OS maintains the illusion of "processes". Each process is isolated; in particular, they each have their own virtual address space. The hardware knows nothing about any of this; all it knows is that somebody keeps overwriting the register file, and changing the page table pointer, causing a (presumably expensive?) flush of the translation look-aside buffers.
Now suppose I'm writing an application, and I want to add database facilities to it.
- I can use something like MySQL, which runs as an external process.
- Because it's an external process, it's isolated from my application.
- A bug in MySQL cannot corrupt my application's memory.
- For that matter, a bug in my application can't corrupt memory belonging to MySQL either.
- Because it's an external process:
- Need to use some kind of IPC mechansim to talk to it. (Unix domain sockets, actual TCP/IP, etc.)
- Requires a system call into the OS kernel.
- Requires a context switch.
- Requires changing virtual address space.
- Because it's an external process, it's isolated from my application.
- I can use something like SQLite, which is a shared library.
- Because it's a shared library, there is no isolation. It's loaded directly into the address space of my application.
- Bugs in SQLite totally can corrupt my application's memory.
- Bugs in my application can corrupt memory belonging to SQLite.
- Because it's a shared library, you can communicate with it by a normal subroutine call.
- No system calls.
- No expensive context switches.
- No expensive address space changes.
- Because it's a shared library, there is no isolation. It's loaded directly into the address space of my application.
Summarising:
- You can use an external process, which is isolated, but slow.
- You can use a shared library, which is fast, but not isolated.
[I'm aware there are lots of additional differences; that's not the focus of this discussion.]
My idea:
- Divide each process into what I'm going to call "zones".
- Each region of the virtual address space belongs to exactly one zone.
- The "current" zone is the zone that the Program Counter currently points to.
- Only memory in the current zone is writable.
(I'm imaging each memory page to have read/write/execute permission bits, but for any page outside the current zone the write-enable bit gets ignored.)
In this way, you could load a shared library but put it in a separate zone.
- Communication is still just normal subroutine calls.
- No expensive context switches.
- No expensive address space changes.
- Code in one zone can't corrupt memory in a different zone.
- Data pointers and code pointers still work across zones.
In short, we have speed and isolation.
There are several ways you could potentially implement this. For example, each page table entry could have a zone number associated with it. Or maybe you have a separate zone table giving the start and end [virtual] address of each zone.
You could allow each page to have arbitrary permissions for each zone. But that seems like it would add too much overhead. Or perhaps you could have just two permission bitmaps per page; one for the "owner" zone, and one for everyone else. There's several ways you could design it.
Is any of this a good idea? Was it ever implemented on any commercial hardware?
[The fact that modern computers don't do this suggests that it's either a terrible idea or it just never caught on.]
int
instructions which were (mis)used for that purpose. Modern CPUs also support PCID which add process tags to the TLB and so the TLB no longer needs to be flushed on address space switches.