Hacker News new | past | comments | ask | show | jobs | submit login

I don't know enough to understand why fork is a bad design. Can you say some about what problems there are?



Microsoft people explained it better than I could, so I link to their explanation: https://www.microsoft.com/en-us/research/publication/a-fork-...


Thanks: Very informative.


The paper that sanxiyn linked is a great read. My summary is that forking and multithreading are a terrible mix. You have two bad options:

- Duplicate all threads. No one does this because it would be total chaos. If another thread was writing to a file or a socket, parts of those writes might happen twice, etc.

- Duplicate only the calling thread. This isn't total chaos, so everyone does this, but this also sucks. Any locks that were held by any other threads will never be released in the new process, so you need to make sure not to touch any locks at all post-fork. But that's a huge restriction, because e.g. malloc() touches locks sometimes. Code that runs post-fork ends up being as restricted as code that runs in signal handlers.


What if the fork syscall failed if there was more than one thread? Would it be bad design then?

I guess the advantage of fork relative to other hypothetical methods of creating child processes is essentially just getting a copy of all the parent's state before the fork. So maybe the question is how often getting a copy of all the parent's state is actually useful?


You'd then have an explicit early-fail footgun instead of a rake in the grass, which has to be an advantage.

However, fork() is still not necessarily a good design, and the reason for that is that the vast majority of the time the reason you want to run fork() is to immediately run exec() afterwards because you're trying to launch a separate process. So, the fork() does all the hard work of duplicating the entire address space of the process, and then exec() throws it all away again.


Makes sense.

One thought is, if the OS/libc was designed so that exec() always created a child process, then you could get the same functionality as fork() by creating a child process using exec() and passing along just the info that the child process needed, possibly using IPC. This seems like better encapsulation, for the same reason you wouldn't write a function which required a ton of arguments it didn't use. It also makes the issue of forking a multithreaded process irrelevant.

But I could imagine this approach being slow if there's a lot of state you want to pass, if copying entire pages of memory (the way fork does?) is significantly faster than than sending the same data via IPC.

I wonder what the most common non-exec() use of fork() is.


fork() doesn't actually copy entire pages of memory. But it does have to copy the page tables (note - someone else might be able to chip in and tell me that Linux has an optimisation for that too). The actual pages are held with a copy-on-write status, so they won't be copied until they are actually modified by one of the processes.

There are quite a lot of things that survive exec(). Not least of which is the set of open files. This is how a shell will pass on the definitions of stdin/stdout/stderr to a command that you want it to run, for example. Also, environment variables survive exec().


>Also, environment variables survive exec().

Does this change depending on whether the environment variable definition was prepended with 'export'?


Thanks for the info!


For Linux kernel's viewpoint where they are trying something new (io_uring_spawn) due to fork's problems, see https://lwn.net/Articles/908268/




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: