Hacker News new | ask | show | jobs
by tsimionescu 847 days ago
If custom process setup code is so common, a better abstraction would have been a CreateProcess() / StartProcess() pair, where CreateProcess() would return a struct that exposes all the necessary methods to control security, FD behavior, working directory etc, and StartProcess() would take that struct and actually run it.
1 comments

The setup part needs to be turing-complete. A simple struct won't do. What you're talking about sounds more like posix_spawn. io_uring_spawn on the other hand would allow that because the parent process could execute the logic between syscall submissions to the newly created process.
What you need is some kind of handle with the ability to do system calls on behalf of another process via that handle. CreateProcess would return this handle, whereas StartProcess would actually use that handle to start execution.

Windows and Fuchsia are two handle-based OSes, and Fuchsia does in fact spawn processes in this manner (Windows just has a family of CreateProcess methods that do both).

This would probably be a cleaner approach, that doesn't duplicate all process functionality between syscalls and the Process struct I was thinking of, and it also is far easier to keep backwards compatible.
If you can do syscalls on behalf of another process, sure. But that's more than "just" CreateProcess.
Ultimately, after exec(), a process only inherits a limited number of things from before exec(). Those things should be directly configurable from the parent process before Start(), using the Turing complete language of the parent process, in my proposal.

If you, say, want to open a socket that will be persisted after exec(), you can open the socket in the original process and pass it in the list of open FDs of the Process object returned by CreateProcess(). After StartProcess() is called, the newly created process should see this open FD as one of its already opened FDs, just like if you had opened it between fork() and exec().

What am I missing? What could you do after fork() in the child that would persist after exec() but not be easily configurable from the outside in principle?

In practice, I can of course imagine that the kernel has all sorts of assumptions about who gets to access certain internal process data structures that it would be very hard to modify today.

The problem is that you end up with two syscalls for every setting. e.g. chroot to change your own root, then newproc_chroot to change the root of a newly spawned process. Start making a list of all the ways you can change process state and you'll realize there are probably hundreds, here. Now imagine being the kernel author and having to duplicate all that code.

Having used both, I can easily say that CreateProcess is deficient, and fork/exec is kind of genius in how many things it makes possible. Could Windows fix CreateProcess with more API? Sure, but they didn't, probably because nobody wanted to spend the effort duplicating all their kernel code.

> The problem is that you end up with two syscalls for every setting. e.g. chroot to change your own root, then newproc_chroot to change the root of a newly spawned process.

Or you add just one syscall to get the handle for the current process and then make all of the syscalls like chroot a userspace alias for proc_chroot(curprocess(), new_root).

The thing is not about whether it's theoretically possible to configure it but more about evolving OS APIs and all those APIs having to be mirrored in the process construction API. E.g. all the security/namespacing stuff.