When Windows and Linux each have a different way of doing a common operation, and a paper where the first author is a Microsoft employee says the Linux way was a mistake, my inclination is to be really skeptical.
On Darwin fork() doesn't necessarily give you a complete environment - the cloned process is essentially only intended for a few basic operations prior to calling exec()
So it isn't just windows and linux having a different way, Darwin goes its own way as well :D
That said, if you read the paper it details all the known problems of fork:
* It isn't thread safe
* It doesn't compose - every library on the address space must support fork
* It breaks basic security features - ASLR being the most obvious one, but there are sandbox issues (Darwin), pledges (BSD), capabilities (Fuchsia)
The paper also details the myriad performance problems of the API.
fork/exec is also very slow due to the need to duplicate enough OS data structures to support the forked child despite it not being useful in the general case to separate these. Even POSIX recognizes this and specifies posix_spawn() which is faster than fork/exec on all POSIX environments.
posix_spawn is just a wrapper around vfork and exec. And crucially, you have it in addition to them, not instead of them, because sometimes it can't do what you need.
> An ad hominem is not a strong argument to dismiss the claim.
I'm not dismissing the claim based on it. I'm just going in skeptical since the author has an incentive for people to believe this conclusion, even if it isn't true. If a paper claims that smoking is a net benefit to your health, wouldn't you want to know that it was funded by tobacco companies?
> Did reading the paper change your opinion?
No, it didn't. Here's a few counterarguments:
* "Fork is no longer simple" - there's a lot of things you might want to do between fork and exec, and it's really not simple to have a different way to do all of them: https://lwn.net/Articles/360556/
* "Fork encourages memory overcommit" - this makes the implicit assumption that overcommit is bad, but this isn't established to be the case.
* "fork restricts the definition of a process to a single address space" - but the only difference between processes and threads is whether or not they share an address space.
* "Fork infects an entire system" - like the GPL infects an entire codebase? If something is useful, it's not necessarily a bad thing that everything will support it.
* "However, the reasons that motivated multi-process servers are long gone" - The prefork MPM isn't deprecated and there's still reasons it needs to be used sometimes today.
> Linux has posix_spawn now
posix_spawn isn't a syscall; it's a userspace function implemented by using vfork and exec internally. But if all you had were posix_spawn, there'd be no reasonable way to implement fork in terms of it, despite the fact that there are some uses for it.
Are subshells and multiprocess servers really less than 0.1% of forks on most UNIX and Linux systems? And I'm sure there's more things than them that fork without execing; they're just the two that came to mind immediately.
Dumping memory state to disk (e.g. how Redis works) is another example of fork without exec.
Additionally, it can also be used in languages that don't have good support for threading (e.g. PHP) to implement parallel processing with shared memory.
However, I'd say that Redis use case is probably the only legit use case (today) of fork without execing, where benefits are huge, and there is no good replacement.
fork gives you multiprocessing, in which you set up a computational environment then create copies of it as needed (e.g., up to the processor parallelism available) to do more work in less time, and without the need to copy or lock data that is independently mutated by each fork. This is a powerful paradigm when there's little need for communication between distinct environments after the initial set-up (collating output may have its challenges, though)
fork+exec gives you spawn. Last I knew, process creation on Linux with fork+exec is faster than process creation on Windows anyway, so, y'know, what-ever.
on the other hand, sometimes (albeit rarely) you need to do specific handling after fork but before exec. posix_spawn, somewhat modeled after windows spawn I gather, lets you do specific limited actions. I don't know the state of affairs now, but many years ago the Python implementation of popen() had to spawn a specific "popen helper program" to perform the necessary file descriptor manipulations; the same sorts of hassles would exist if you were restricted to posix_spawn but needed to do some action that can't be represented as a file action or a spawn attribute.
There should have been a fork+exec that would also do all the stuff everybody is obliged to code, anyway, between them.
Some programs would still fork, grovel around, and exec if the combined thing didn't do everything they need exactly the way they need, but the copying page tables and file descriptors, and marking everything read-only, is mostly wasted, as is the parent process needing to fault on all those pages. I wonder whether, if a COW page drops back to one reference, it is also marked back to writable, or if the fault has to happen anyway.
When Windows and Linux each have a different way of doing a common operation, and a paper where the first author is a Microsoft employee says the Linux way was a mistake, my inclination is to be really skeptical.
On Darwin fork() doesn't necessarily give you a complete environment - the cloned process is essentially only intended for a few basic operations prior to calling exec()
So it isn't just windows and linux having a different way, Darwin goes its own way as well :D
That said, if you read the paper it details all the known problems of fork:
* It isn't thread safe
* It doesn't compose - every library on the address space must support fork
* It breaks basic security features - ASLR being the most obvious one, but there are sandbox issues (Darwin), pledges (BSD), capabilities (Fuchsia)
The paper also details the myriad performance problems of the API.
fork/exec is also very slow due to the need to duplicate enough OS data structures to support the forked child despite it not being useful in the general case to separate these. Even POSIX recognizes this and specifies posix_spawn() which is faster than fork/exec on all POSIX environments.
posix_spawn is just a wrapper around vfork and exec. And crucially, you have it in addition to them, not instead of them, because sometimes it can't do what you need.
An ad hominem is not a strong argument to dismiss the claim.
Did reading the paper change your opinion?
It also isn’t “the Linux way”, it’s “the Unix way”. Linux has posix_spawn now, but, reading the paper, apparently lots of tools don’t use them ¿yet?.
> An ad hominem is not a strong argument to dismiss the claim.
I'm not dismissing the claim based on it. I'm just going in skeptical since the author has an incentive for people to believe this conclusion, even if it isn't true. If a paper claims that smoking is a net benefit to your health, wouldn't you want to know that it was funded by tobacco companies?
> Did reading the paper change your opinion?
No, it didn't. Here's a few counterarguments:
* "Fork is no longer simple" - there's a lot of things you might want to do between fork and exec, and it's really not simple to have a different way to do all of them: https://lwn.net/Articles/360556/
* "Fork encourages memory overcommit" - this makes the implicit assumption that overcommit is bad, but this isn't established to be the case.
* "fork restricts the definition of a process to a single address space" - but the only difference between processes and threads is whether or not they share an address space.
* "Fork infects an entire system" - like the GPL infects an entire codebase? If something is useful, it's not necessarily a bad thing that everything will support it.
* "However, the reasons that motivated multi-process servers are long gone" - The prefork MPM isn't deprecated and there's still reasons it needs to be used sometimes today.
> Linux has posix_spawn now
posix_spawn isn't a syscall; it's a userspace function implemented by using vfork and exec internally. But if all you had were posix_spawn, there'd be no reasonable way to implement fork in terms of it, despite the fact that there are some uses for it.
The fact that 99.9+ invocations of fork() are immediately followed by exec() shows that the abstraction is a mistake.
Unix would have been better if execution thread and address space had been separate abstractions.
Are subshells and multiprocess servers really less than 0.1% of forks on most UNIX and Linux systems? And I'm sure there's more things than them that fork without execing; they're just the two that came to mind immediately.
Dumping memory state to disk (e.g. how Redis works) is another example of fork without exec.
Additionally, it can also be used in languages that don't have good support for threading (e.g. PHP) to implement parallel processing with shared memory.
However, I'd say that Redis use case is probably the only legit use case (today) of fork without execing, where benefits are huge, and there is no good replacement.
I think you mean execution process, not thread.
That said, processes should not share virtual address spaces by default. Sharing memory between processes should be an explicit choice.
(Or should 99.9% of calls to spawn() be followed by new_address_space()? )
The researchers can write their own single address space os if they are so inclined.
fork was one heck of a great mistake.
fork gives you multiprocessing, in which you set up a computational environment then create copies of it as needed (e.g., up to the processor parallelism available) to do more work in less time, and without the need to copy or lock data that is independently mutated by each fork. This is a powerful paradigm when there's little need for communication between distinct environments after the initial set-up (collating output may have its challenges, though)
fork+exec gives you spawn. Last I knew, process creation on Linux with fork+exec is faster than process creation on Windows anyway, so, y'know, what-ever.
on the other hand, sometimes (albeit rarely) you need to do specific handling after fork but before exec. posix_spawn, somewhat modeled after windows spawn I gather, lets you do specific limited actions. I don't know the state of affairs now, but many years ago the Python implementation of popen() had to spawn a specific "popen helper program" to perform the necessary file descriptor manipulations; the same sorts of hassles would exist if you were restricted to posix_spawn but needed to do some action that can't be represented as a file action or a spawn attribute.
They would probably think different if fork() was fast on windows.
There should have been a fork+exec that would also do all the stuff everybody is obliged to code, anyway, between them.
Some programs would still fork, grovel around, and exec if the combined thing didn't do everything they need exactly the way they need, but the copying page tables and file descriptors, and marking everything read-only, is mostly wasted, as is the parent process needing to fault on all those pages. I wonder whether, if a COW page drops back to one reference, it is also marked back to writable, or if the fault has to happen anyway.
You're after posix_spawn(2) :D
I don't think i've ever seen posix_spawn(3) in a serious project. Maybe on a small embedded system lacking MMU support?
1 reply →
Much bigger mistake is the need for parent to wait for its children si they can be cleaned up. Isn't that what the OS is for?