fork()
говорит: «скопируйте текущее состояние процесса в новый процесс и запустите его прямо здесь».Поскольку код выполняется в двух процессах, он фактически возвращается дважды: один раз в родительском процессе (где он возвращает идентификатор процесса дочернего процесса) и один раз в дочернем (где он возвращает ноль).
ТамЕсть множество ограничений на то, что безопасно вызывать в дочернем процессе после fork()
(см. ниже).Ожидается, что вызов fork()
был первой частью процесса запуска нового процесса, запускающего новый исполняемый файл с собственным состоянием.Вторая часть этого процесса - это вызов execve()
или один из его вариантов, который указывает путь к исполняемому файлу, который будет загружен в текущий запущенный процесс, аргументы, которые должны быть предоставлены этому процессу, и переменные окружения, которые его окружают.процесс.(Ничто не мешает вам повторно выполнить исполняемый в данный момент исполняемый файл и предоставить флаг, который позволит ему выбрать, где остановился родитель, если это то, что вы действительно хотите.)
UNIX fork()-exec()
танец примерно эквивалентен Windows CreateProcess()
.Более новая функция еще больше похожа на это: posix_spawn()
.
В качестве практического примера использования fork()
рассмотрим оболочку, например bash
.fork()
все время используется командной оболочкой.Когда вы указываете оболочке запустить программу (например, echo "hello world"
), она разветвляется и затем запускает эту программу.Конвейер представляет собой набор разветвленных процессов с stdout
и stdin
, соответствующим образом подключенными родительским процессом между fork()
и exec()
.
Если вы хотите создать новый поток, вам следует использоватьбиблиотека потоков Posix.Вы создаете новую тему Posix (pthread), используя pthread_create()
.Ваш пример CreateNewThread()
будет выглядеть следующим образом:
#include <pthread.h>
/* Pthread functions are expected to accept and return void *. */
void *MyFunctionToRun(void *dummy __unused);
pthread_t thread;
int error = pthread_create(&thread,
NULL/*use default thread attributes*/,
MyFunctionToRun,
(void *)NULL/*argument*/);
До того, как стали доступны потоки, fork()
была самой близкой вещью, которую UNIX предоставил для многопоточности.Теперь, когда потоки доступны, использование fork()
почти полностью ограничено порождением нового процесса для выполнения другого исполняемого файла.
ниже: ограничения заключаются в том, что fork()
предшествует многопоточности, поэтому только поток, вызывающийfork()
продолжает выполняться в дочернем процессе.По POSIX :
Процесс должен быть создан с одним потоком.Если многопоточный процесс вызывает fork (), новый процесс должен содержать реплику вызывающего потока и всего его адресного пространства, возможно, включая состояния мьютексов и других ресурсов.Следовательно, чтобы избежать ошибок, дочерний процесс может выполнять только асинхронно-безопасные операции до тех пор, пока не будет вызвана одна из функций exec.[THR] [Option Start] Обработчики вил можно установить с помощью функции pthread_atfork (), чтобы поддерживать инварианты приложения в вызовах fork ().[Option End]
Когда приложение вызывает fork () из обработчика сигнала, а любой из обработчиков fork, зарегистрированных pthread_atfork (), вызывает функцию, которая не безопасна для асинхронного сигнала, поведение не определено.
Поскольку любая библиотечная функция, которую вы вызываете, могла породить поток от вашего имени, параноидальное предположение состоит в том, что вы всегда ограничены выполнением асинхронно-безопасных операций в дочернем процессе между вызовом fork()
иexec()
.