Hacker News new | ask | show | jobs
by tsimionescu 851 days ago
Well, there are two categories of problems.

One is that fork() is by definition a very costly operation (a copy of the entire address space of the current process), and the kernel has to do a lot of work to implement it efficiently (implementing copy-on-write clones of all of the pages of a process). And that all that work is done for nothing in the very very very common case of doing fork() + exec().

The other problem is that the semantics of fork() just fundamentally can't work properly for a multi-threaded process. In any multi-threaded process, if you do fork(), the only thing you can safely do in the child process is to call exec(). Any other call, even a printf() or some path logic, has a very good chance to lead to a deadlock, quite possibly inside malloc() itself.

So fork() as a standalone operatikn is actually an extremely niche utility (duplictaing single-threaded processes) that has been made the main way of spawning new processes. Similarly, exec() by itself is an even more niche utility, sometimes useful for "launcher" style processes.

So, instead of achieving an extremely common task (launch some binary file as a new process) using a dedicated system call, Unix has chosen to define two extremely niche syscalls that you should almost never use individually, but that together can implement this common process, but only with a lot of behind the scenes work to make it efficient.

4 comments

The way the shell works is an essential part of Unix design, and the fork/exec pair suits it very well. In the forked child process before the exec, the shell can execute arbitrary code that inherits all file descriptors and manipulates/redirects them in a certain way.
I didn't say it's useless, I said it's a niche utility. There are, what, 10 even slightly commonly used shells?

Compared to the thousands of other programs that spawn processes, I think shells count as a niche use.

If fork() and exec() had been kept as the niche utilities they are in addition to a more commonly used spawn() syscall, fork() and exec() could have remained simple and small, and not needed all the CoW magic and many other complex features at all.

I wonder now if one could emulate fork-shenanigans-exec by:

- clone CLONE_FILES with AF_UNIX socketpair

- send file descriptors through the socket

- do whatever was in shenanigans (you have the socket for communication/RPC/whatever)

- exec

or maybe even easier:

- clone CLONE_FILES with pipe or two

- do whatever was in shenanigans (you have the pipe for communication/RPC/whatever)

- exec

> One is that fork() is by definition a very costly operation

Isn't process creation much slower on Windows (not using fork) than forking on Unix-likes?

It is, but there are many other differences between Unix and Windows processes than just fork() VS CreateProcess().

Even disregarding this, the actual fork() syscall has had many hundreds of dev hours poured into it to implement the costly semantics (copy all resources) in an efficient way for the common use case (copy-on-write semantics).

What do people who care a lot about latency do about this? Do they have no choice but to eat the cost of fork()?
They avoid spawning new processes.
Also, fork() interacts very poorly with e.g. CUDA: turns out, you can't fork the GPU itself.