The general problem with making fork() work in a multi-threaded world is what to do with all of the threads. There are two alternatives. One is to copy all of the threads into the new process. This causes the programmer or implementation to deal with threads that are suspended on system calls or that might be about to execute system calls that should not be executed in the new process. The other alternative is to copy only the thread that calls fork(). This creates the difficulty that the state of process-local resources is usually held in process memory. If a thread that is not calling fork() holds a resource, that resource is never released in the child process because the thread whose job it is to release the resource does not exist in the child process.
If we call fork(2)
in
a multi-threaded environment the thread doing the call is now the main-thread in the new process and all the other threads, which ran in the parent process, are dead. And everything they did was left exactly as it was just before the call to fork(2)
.
Now imagine that these other threads were happily doing their work before the call to fork(2)
and a couple of milliseconds later they are
dead. What if something these now-dead threads did was not meant to be left exactly as it was?
Let me give you an example. Let's say our main thread (the one which is going to call fork(2)
) was sleeping while we had lots of other threads
happily doing some work. Allocating memory, writing to it, copying from it, writing to files, writing to a database and so on. They were probably allocating memory with something like malloc(3)
.
Well, it turns out that malloc(3)
uses a mutex internally to guarantee thread-safety. And exactly this is the problem.
What if one of these threads was using malloc(3)
and has acquired the lock of the mutex in the exact same moment that the main-thread called fork(2)
?
In the new child process the lock is still held - by a now-dead thread, who will never return it.
The new child process will have no idea if it's safe to use malloc(3)
or not. In the worst case it will call malloc(3)
and
block until it acquires the lock, which will never happen, since the thread who's supposed to return it is dead. And this is just malloc(3)
.
Think about all the other possible mutexes and locks in database drivers, file handling libraries, networking libraries and so on.