Книга: Programming with POSIX® Threads
6.1 fork
Avoid using fork in a threaded program (if you can) unless you intend to exec a new program immediately.
When a threaded process calls fork to create a child process, Pthreads specifies that only the thread calling fork exists in the child. Although only the calling thread exists on return from fork in the child process, all other Pthreads states remain as they were at the time of the call to fork. In the child process, the thread has the same thread state as in the parent. It owns the same mutexes, has the same value for all thread-specific data keys, and so forth. All mutexes and condition variables exist, although any threads that were waiting on a synchronization object at the time of the fork are no longer waiting. (They don't exist in the child process, so how could they be waiting?)
Pthreads does not "terminate" the other threads in a forked process, as ifthey exited with pthread_exit or even as if they were canceled. They simply cease to exist. That is, the threads do not run thread-specific data destructors or cleanup handlers. This is not a problem if the child process is about to call exec to run a new program, but if you use fork to clone a threaded program, beware that you may lose access to memory, especially heap memory stored only as thread-specific data values.
The state of mutexes is not affected by a fork. If it was locked in the parent it is locked in the child!
If a mutex was locked at the time of the call to fork, then it is still locked in the child. Because a locked mutex is owned by the thread that locked it, the mutex can be unlocked in the child only if the thread that locked the mutex was the one that called fork. This is important to remember — if another thread has a mutex locked when you call fork, you will lose access to that mutex and any data controlled by that mutex.
Despite the complications, you can fork a child that continues running and even continues to use Pthreads. You must use fork handlers carefully to protect your mutexes and the shared data that the mutexes are protecting. Fork handlers are described in Section 6.1.1.
Because thread-specific data destructors and cleanup handlers are not called, you may need to worry about memory leaks. One possible solution would be to cancel threads created by your subsystem in the prepare fork handler, and wait for them to terminate before allowing the fork to continue (by returning), and then create new threads in the parent handler that is called after fork completes. This could easily become messy, and I am not recommending it as a solution. Instead, take another look at the warning back at the beginning of this section: Avoid using fork in threaded code except where the child process will immediately exec a new program.
POSIX specifies a small set of functions that may be called safely from within signal-catching functions ("async-signal safe" functions), and fork is one ofthem. However, none of the POSIX threads functions is async-signal safe (and there are good reasons for this, because being async-signal safe generally makes a function substantially more expensive). With the introduction offork handlers, however, a call to fork is also a call to some set of fork handlers.
The purpose of a fork handler is to allow threaded code to protect synchronization state and data invariants across a fork, and in most cases that requires locking mutexes. But you cannot lock mutexes from a signal-catching function. So while it is legal to call fork from within a signal-catching function, doing so may fbeyond the control or knowledge of the caller) require performing other operations that cannot be performed within a signal-catching function.
This is an inconsistency in the POSIX standard that will need to be fixed. Nobody yet knows what the eventual solution will be. My advice is to avoid using fork in a signal-catching function.
- Листинг 3.3. (fork.c) Ветвление программы с помощью функции fork()
- 9.1.1. Создание процесса: fork()
- 9.1.1.1. После fork(): общие и различные атрибуты
- 10.9. Сигналы, передающиеся через fork() и exec()
- Вызов функции fork()
- 6.1.1 Fork handlers
- 9.3.9 Fork handlers
- 10.4.4. Ускоренное создание процессов с помощью vfork()
- 3.2.2. Функции fork() и exec()
- Совместное использование функций fork() и exec()
- Листинг 3.4. (fork-exec.с) Совместное использование функций fork() и exec()
- 9.1.2.1 Выгрузка при выполнении системной функции fork