Since you mention fork... never thought what kind of awful code this particular high-maintenance design causes?
As a first example, think about the ugly hack that vfork is. It still survives because it does perform better than fork. Luckily posix_spawn hides this ugliness and chooses fork over vfork. On the other hand, it means that popen can be _much_ more expensive than system just because one will use fork and the other will use vfork.
And one thing I realized today: you can hardly retrieve the errno of a failed exec system call in a forked process. Everything you will do is going to be racy, except possibly using ptrace on the child.
(FWIW, the most clever way I thought about it is to use a FD_CLOEXEC pipe and write the return code of exec there; if the parent cannot read anything, exec succeeded... or the forked child died of a signal after exec returned but before it wrote to a pipe... and if you wanna use waitpid, you get it wrong in case the execed process was signaled before the parent started waitpid...).
Ghosts of Unix past, part 4: High-maintenance designs
Posted Dec 4, 2010 18:00 UTC (Sat) by nix (subscriber, #2304)
[Link]
Yeah, that's quite nasty. But spawn*() has all the same problems and a bunch of extra ones, prominent among them the fact that it's exactly the same as fork()/exec() with the code between the fork() and exec() hardwired. So you end up with dozens of spawn*() calls and no benefit over fork()/exec() at all (except on tiny non-MMU systems, which can theoretically implement spawn*() but not fork()/exec() --- but usually implement both, because most code uses fork()/exec() and not spawn*().)
I suspect that a combination of waitpid() (to catch signals and read-from-pipe to catch errno might work: if you played games with self-signalling you could possibly encode errnos as rare signals and drop the pipe, at the cost of losing the ability to detect those rare signals.
A bit of extra effort (sending something down the pipe right before exec() as well as right after a failed one, and opening the pipe end O_CLOEXEC) enables you to distinguish between a signal hitting before exec() and a signal hitting after a successful exec().
But, yes, this is all pointlessly complex. If C had proper Lisp-style macros we could wrap this up in a library without the result becoming as inflexible as spawn(). (Something involving function pointers could get halfway there, perhaps. But it wouldn't be as neat.)
Ghosts of Unix past, part 4: High-maintenance designs
Posted Dec 5, 2010 20:40 UTC (Sun) by quotemstr (subscriber, #45331)
[Link]
except on tiny non-MMU systems, which can theoretically implement spawn*() but not fork()/exec()
Cygwin also falls into this category. fork() works there, but it's painfully slow because it copies the entire address space. spawn() is far more efficient.
Ghosts of Unix past, part 4: High-maintenance designs
Posted Dec 6, 2010 10:58 UTC (Mon) by pbonzini (subscriber, #60935)
[Link]
no benefit over fork()/exec() at all (except on tiny non-MMU systems, which can theoretically implement spawn*() but not fork()/exec()
Actually, if the parent has a large RSS it is quite common to see major performance improvements with vfork() over fork(). And given how hacky vfork() is, I'd really be happy to pay the price of spawn()'s inflexibility.
fork() should be treated like a relic of when parallelism was achieved using processes rather than threads, IMO.
Ghosts of Unix past, part 4: High-maintenance designs
Posted Dec 6, 2010 11:31 UTC (Mon) by dlang (✭ supporter ✭, #313)
[Link]
does this vfork advantage still exist (i.e., is it measurable) when the host OS does Copy On Write for the fork instead of actually copying all ram?
yes, the page tables still get modified twice, but is this measurable on modern hardware?
Ghosts of Unix past, part 4: High-maintenance designs
Posted Dec 6, 2010 11:58 UTC (Mon) by pbonzini (subscriber, #60935)
[Link]
Yes, I've seen forking take 60% of CPU (that was forking 4 child processes per second from a +2 GB process). Using fork to vfork, or equivalently switching to posix_spawn, brought it down to 3-4%.