What happens if you want the child to inherit other fds?
Or set other attributes such as the session, euid, egid etc?
The elegance of fork() is in avoiding the complexity of requiring every attribute of the process to be explicitly stated in order to create a new process. We simply inherit our parent's attributes and if that turns out not be desirable it is fixed in the child with the usual syscalls.
Your "avoiding the complexity" in fact turns into its mirror image: in order to ensure sane behaviour, you have to have a preamble to avoid inheriting things you don't want. This results in people writing things like http://www.linuxprogrammingblog.com/threads-and-fork-think-t... : the list of things you have to remember not to use in order to use the "elegant" API.
I'm not positive that it's elegant. It may also be considered an ugly hack (not sure either), and I'm pretty certain it's been responsible for many security holes.
It would be interesting to see in what ways Unix (especially shell scripts) would be different if processes would not inherit all the baggage by default.
Attributes is just another word for "global state". When we speak of elegance in computing, it often means functional programming languages where eradicating global state is the primary motivation.
So really fork is the opposite of elegance. It makes state implicit, not explicit.
That may have been fine in 1970, when there was very little to inherit (no thread handles, no named pipes, no semaphores, IIRC) and security wasn’t a big concern.
Nowadays, I think ‘whitelisting’ what the new process can do is the choice to make, not forking and then (hopefully) ‘blacklisting’ what you don’t want (that’s especially important if you eventually will be running code you didn’t write or, maybe, don’t even have source for)
That also is easier to test for. If you forget to specify a capability before forking, the bugs you see will be better reproducible than when you forget a thing you don’t need.
I can’t find it now, but try googling an article on how to properly fork a process nowadays. It is insanely difficult to do right.
The elegance of fork() is in avoiding the complexity of requiring every attribute of the process to be explicitly stated in order to create a new process. We simply inherit our parent's attributes and if that turns out not be desirable it is fixed in the child with the usual syscalls.
EDIT: s/And/Or set/