Использование fork
и exec
иллюстрирует дух UNIX в том смысле, что он обеспечивает очень простой способ запуска новых процессов.
Вызов fork
в основном создает копию текущего процесса, идентичную в почти во всех отношениях. Не все копируется (например, ограничения ресурсов в некоторых реализациях), но идея состоит в том, чтобы создать максимально близкую копию.
Новый процесс (дочерний) получает другой идентификатор процесса (PID) и имеет PID старого процесса (родительского) в качестве родительского PID (PPID). Поскольку два процесса теперь выполняют точно один и тот же код, они могут определить, какой именно, с помощью кода возврата fork
- дочерний элемент получает 0, родительский получает PID дочернего элемента. Это все, конечно, если предположить, что вызов fork
работает - если нет, дочерний элемент не создается, а родитель получает код ошибки.
Вызов exec
- это способ в основном заменить весь текущий процесс новой программой. Он загружает программу в текущее пространство процесса и запускает ее из точки входа.
Итак, fork
и exec
часто используются последовательно, чтобы новая программа работала как дочерний элемент текущего процесса. Оболочки обычно делают это всякий раз, когда вы пытаетесь запустить такую программу, как find
- оболочка разветвляется, затем дочерний элемент загружает в память программу find
, настраивая все аргументы командной строки, стандартный ввод-вывод и т. Д.
Но их не обязательно использовать вместе. Для программы вполне допустимо fork
сама без exec
, если, например, программа содержит как родительский, так и дочерний код (вам нужно быть осторожным в том, что вы делаете, у каждой реализации могут быть ограничения). Это довольно часто использовалось (и остается) для демонов, которые просто прослушивают порт TCP и fork
свою копию для обработки определенного запроса, в то время как родитель возвращается к прослушиванию.
Точно так же, программы, которые знают, что они закончили и просто хотят запустить другую программу, не должны fork
, exec
и затем wait
для ребенка. Они могут просто загрузить ребенка прямо в пространство процесса.
Некоторые реализации UNIX имеют оптимизированный fork
, который использует то, что они называют копированием при записи. Это хитрость, чтобы отложить копирование пространства процесса в fork
до тех пор, пока программа не попытается что-то изменить в этом пространстве. Это полезно для тех программ, которые используют только fork
, а не exec
, поскольку им не нужно копировать все пространство процесса.
Если exec
- это , который вызывается следующим образом fork
(и именно это происходит в основном), это вызывает запись в пространство процесса и затем копируется для дочернего процесса.
Обратите внимание, что существует целое семейство exec
вызовов (execl
, execle
, execve
и т. Д.), Но exec
в контексте здесь означает любой из них.
Следующая диаграмма иллюстрирует типичную операцию fork/exec
, в которой оболочка bash
используется для отображения каталога с помощью команды ls
:
+--------+
| pid=7 |
| ppid=4 |
| bash |
+--------+
|
| calls fork
V
+--------+ +--------+
| pid=7 | forks | pid=22 |
| ppid=4 | ----------> | ppid=7 |
| bash | | bash |
+--------+ +--------+
| |
| waits for pid 22 | calls exec to run ls
| V
| +--------+
| | pid=22 |
| | ppid=7 |
| | ls |
V +--------+
+--------+ |
| pid=7 | | exits
| ppid=4 | <---------------+
| bash |
+--------+
|
| continues
V