Live data from Hacker News

Moving beyond fork() + exec()

lwn.net

231–240 of 358 posts

Re: Moving beyond fork() + exec()

#231
post #113
post #29

Earlier quoted context omitted.

I have the entirely opposite opinion. IMO a big mistake of the UNIXy model is that so much state is preserved across the creation of a process. For example, there are APIs to have a specific thing be fd number 4 so you can run a program and have it find that thing at fd 4. This is weird . Windows, for all its many, many faults, did not use fork+exec and instead mostly has options for how one creates a process. It was…

Well, a lot of the power of the UNIX shell comes form this and I see this as a major advantage over Windows. So no, I do not think Windows got it right. Any kind of replacement should aim for the same conceptual simplicity and power. Sadly, I fear that people driving development nowadays are more interested in building unbreakable walled gardens for advertisement or app stores, or trying to squeeze down the some smal…

Nothing about the UNIX shell is reliant on the fork model. Windows processes have stdio handles as well.

Re: Moving beyond fork() + exec()

#232
post #229

Earlier quoted context omitted.

I'm not talking about glibc implementation details. I'm talking about how mixing fork(2) with threads creates harmful race conditions. The forked child has only 1 thread in its process. If the parent's threads are holding a lock or are in the middle of mutating a shared data structure, you're fucked, because those threads are no longer running in your child's copy of the address space and will not finish their work.…

Again, you're talking about userspace now. Not kernel-imposed constraints. A userspace program is always free to deadlock itself; fork doesn't change that.

I never said it was a kernel imposed constraint. It remains unsafe behavior, and frankly you'd be stupid to ignore it if you want to write a stable multi threaded program. In colloquial shorthand, you can't do it.

Signal safety is not the same as this, but similar. I believe posix specifies what is signal-unsafe to be overly broad. But the unsafety isn't an illusion -- it's an emergent property from something being a bad idea given the primitives at work, there are broad categories of bugs that are easy to introduce due to the way it works. So for signals, posix declares a bunch of ill advised things to be undefined, and with good reason. This is an analogous scenario.

Re: Moving beyond fork() + exec()

#233
post #231
post #113

Earlier quoted context omitted.

Well, a lot of the power of the UNIX shell comes form this and I see this as a major advantage over Windows. So no, I do not think Windows got it right. Any kind of replacement should aim for the same conceptual simplicity and power. Sadly, I fear that people driving development nowadays are more interested in building unbreakable walled gardens for advertisement or app stores, or trying to squeeze down the some smal…

Nothing about the UNIX shell is reliant on the fork model. Windows processes have stdio handles as well.

A lot of features of UNIX shells are build around pipe and dup and the fork + exec model. One can certainly implement in differently, but it is - like UNIX in general - very nice and elegant.

Re: Moving beyond fork() + exec()

#234
post #227
post #149

Earlier quoted context omitted.

True, but on Windows the approach is then to use COM servers, which have a faster IPC model, and can even serve multiple clients, depending on how the appartement space is configured.

If you want the isolation features of a separate process, you can’t substitute it with a single multithreaded COM server process. .NET tried this with app domains, which are now deprecated.

App Domains were in process, which isn't was I am talking about with outproc COM.

Also App Domains are partially back in .NET Core, isolation features aren't there, but code unloading is, via AssemblyLoadContext.

Re: Moving beyond fork() + exec()

#235
post #195
post #194

Earlier quoted context omitted.

Than UNIX fork/exec model, or calling into Create Process all the time. Windows has a more rich set of IPC stuff than POSIX, especially since it has a microkernel like design. If you are going to say it is everything on the same memory space anyway, it isn't. Optional on Windows 10, and enforced on Windows 11, Hyper-V is always running, and several components including kernel and driver modules are sandboxed into the…

fork/exec is not an IPC model...

It actually kind of is, hence why you have information about parent/child and get to share memory.

This is how a http server back in the day would share the request context for the child process to reply back.

Re: Moving beyond fork() + exec()

#236
post #70

Earlier quoted context omitted.

It is somewhat interesting that the most widely used "big" OS that doesn't use fork, i.e. Windows, has dog slow process creation... I agree that there should be non-fork primitives, I'm just not that sure that performance is the best argument.

Because that OS best practices is to use threads. Traditionally Windows applications that create processes all the time come from UNIX heritage. Contrary to UNIX, Windows NT was designed with threads first mentality, from the get go. While on UNIX they were added after fact, and to this day there are gotchas mixing posix threads with signals, fork and exec.

POSIX threads having problems with signals is, imho, mostly the problem with signals in general. They are pretty poorly designed: https://lwn.net/Articles/414618/

Re: Moving beyond fork() + exec()

#237
post #26

The elegance of the fork() + exec() model is that every kind of configuration can be done after the fork using all the usual APIs. Every attempt to replace it with a combined call that I have seen so far seemed fundamentally poorer because it needs to add all configuration options as parameters to the call and then do this in away that you can extend it later and does not become a mess.

I kinda disagree, though I do see the usefulness here. While fork/exec can be useful in some cases, it'd be honestly pretty neat if the APIs took a pidfd argument (maybe with 0 meaning current process). Only program is setuid/setgid binaries I suppose but maybe this case is better handled by special casing `exec`.

For example

   pidfd_t ps = spawn(); // creates a process stopped (kernel does this anyway by default)
   setuid(ps, 33);
   capset(ps, ...);
   socket(ps, ...);
   mmap(ps, ...);
   process_vm_writev(ps, ...);
   exec(ps, ...);
   signal(ps, SIGCONT);
   // error handling elided
I guess this is a little bit me being a bit of critical of the usual syscall APIs for not thinking about "what if I want to do this to another process I have access to" but...

It also makes things like thread safety even reasonably doable with fork. I do agree though that stuff like CreateProcess which take in a gazillion parameters don't really make for the greatest of userspace APIs

Re: Moving beyond fork() + exec()

#238
post #178

Earlier quoted context omitted.

Or you could call ptrace_syscall (that doesn't currently exist) on your child processes that you are tracing because you'd always be tracing them by default, or get an io_uring for the child process, or...

A ptrace_syscall would be interesting and would seem to be a full replacement for having the pid argument everywhere. But frankly, I am not really seeing the value.

The value is not needing to change every other syscall and not needing to write new ones with a pid argument (besides which, what when you want to change it to a pidfd argument? then you add pidfd_syscall instead of duplicating every syscall again)

Re: Moving beyond fork() + exec()

#239
post #235
post #195

Earlier quoted context omitted.

fork/exec is not an IPC model...

It actually kind of is, hence why you have information about parent/child and get to share memory. This is how a http server back in the day would share the request context for the child process to reply back.

I would say that pipes and shared memory are the IPC mechanisms? Controlling the state of the exec'd process's file descriptors would counts as a way to set up interprocess communication, but once that's done, it's the pipe or SHM that does the actual communication.

Re: Moving beyond fork() + exec()

#240
post #26

The elegance of the fork() + exec() model is that every kind of configuration can be done after the fork using all the usual APIs. Every attempt to replace it with a combined call that I have seen so far seemed fundamentally poorer because it needs to add all configuration options as parameters to the call and then do this in away that you can extend it later and does not become a mess.

I kinda disagree, though I do see the usefulness here. While fork/exec can be useful in some cases, it'd be honestly pretty neat if the APIs took a pidfd argument (maybe with 0 meaning current process). Only program is setuid/setgid binaries I suppose but maybe this case is better handled by special casing `exec`. For example pidfd_t ps = spawn(); // creates a process stopped (kernel does this anyway by default) setu…

Maybe, a few people proposed this. It is a lot better than a single spawn call.

But how often would one actually need this? And what are the semantics? Refer arguments (e.g. file descriptors) to the current process or the other one? How are cross-permissions handled? It seems a lot of complexity...

Someones proposed a ptrace_syscall which could achieve the same thing.

Post reply on HN