Hacker News new | past | comments | ask | show | jobs | submit login

That's exactly what I understood from the proposal too, but I don't see why that is useful, nor why it'd be worth implementing with WASM.

Perhaps it's worth stepping back. The reason SASOSs are always written in managed languages like C# or Java [dialects] is that they're trying to solve several problems simultaneously:

1. IPC is slow due to all the copying and context switching.

2. Beyond slow it's also awkward to share data structures across processes. You need SHM segments, special negotiations, custom allocators that let you control precisely where data goes and which are thread safe across processes etc. Even if you do it, you need a lot of protocols to get memory management right like IUnknown. So in practice it's rarely done outside of simple and special cases like shared pixel buffers.

3. Hardware processes conflate several different things together that we'd like to unpack, such as fault isolation, privacy, permissions etc.

4. Hard to evolve data structures when code is compiled using C ABIs.

and so on.

Simply creating non-overlapping address spaces doesn't help with any of these things. Even if all you do on a context switch is twiddle permission bits, it doesn't matter: you still need to do a TLB flush and that's the bulk of the cost of the context switch, and at any rate, you can't just call a function in another address space. Even if you know its address, and can allocate some memory to hold the arguments and pointers, you can't jump there because the target is mapped with zero permissions. And even if you go via a trampoline, so what, the stack holding your arguments also isn't readable. If you fix that with more hacks, now any structures the arguments point to aren't readable and so on recursively.

So you end up having to introduce RPC to copy the data structures across. Well, now what if you want a genuinely shared bit of state? You need some notion of handles, proxies, stubs, and that in turn means you need a way to coordinate lifetime management so different quasi-processes don't try to access memory another process freed. That's COM IUnknown::AddRef. Then you need ways to handle loosely coupled components that can be upgraded independently. That's COM IUnknown::QueryInterface and friends. And so on and so forth.

In a SASOS all that goes away because the compiler and GC are tightly integrated, and they don't let you manufacture arbitrary pointers. You don't have to do refcounting or marshalling as a consequence, you can evolve the ABIs of components without breaking things, you can create capabilities easily and cheaply, and so on.

As discussed above, speculation is a pain because it lets you break the rule of not crafting arbitrary pointers, but there are caveats to that caveat. I'm not actually convinced Spectre kills SASOS though you do need to do things differently.




Why can't it be that whatever procedure you use to give/get a pointer into another process also makes the necessary modifications to the page table? As you point out, this would become very tedious to the programmer if you just tried to bolt it on to current languages as a library, but I can imagine, e.g., a version of Java or C# that makes this all mostly seamless.

As for what the benefit is, I think you can at the very least get rid of needing to copy data back and forth.

Not that I'm an advocate for single address space OS's. I'd have to think about this more. You might be right. I'm playing devil's advocate to think it through, not to defend a position, if that makes sense.


Sure.

Well, pages are coarse grained so you'd end up giving the other quasi-process access to more stuff than it should have. And you'd have to flush the TLB so you pay the context switch cost anyway, at which point why bother? The reason operating systems make overlapping mappings is (classically) to enable better memory sharing due to not needing relocations or GOT/PLTs. That's all irrelevant these days due to ASLR (which doesn't even work that well anymore) but that was the idea.

You can do some tricks with using special allocators for the stuff you want to reveal that places the data in SHM segments, then blit the stack across to the other process and it can work. I know this because I built such a system as my undergrad thesis project :)

https://web.archive.org/web/20160331135050/plan99.net/~mike/...

It's very ugly though. And again, you need to coordinate memory management. For FastRPC it didn't matter because it was used mostly for holding stuff temporarily whilst you called into a library, so the 'outer' process owned the memory and the 'inner' process just used it temporarily. I never did anything with complex lifetimes.

One way of thinking about it is to study the issues that cause people to write large JVM apps instead of large C apps. It's not any different at the systems level. They want GC that works across all the libraries they use, they want to be able to upgrade the backend compiler without frontend-recompiling everything, they want to be able to upgrade a library or change a memory layout without recompiling the world, and they don't want all the goop that is triggered by allowing libraries to use arbitrary compilers and memory management subsystems. Forcing a unified compiler and GC simplifies the inter-module protocols so drastically, the wins are enormous.


>Well, pages are coarse grained so you'd end up giving the other quasi-process access to more stuff than it should have.

Good point. Embarrassing oversight on my part. My whole mental model of how this would work has come crashing down. Now obvious to me that to have it work the way I envisioned, you would need a managed-code only environment that supervises all memory accesses.

>I know this because I built such a system as my undergrad thesis project

Very cool!




Consider applying for YC's Summer 2025 batch! Applications are open till May 13

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: