Основы программирования в Linux
Функции
, как правило, не возвращаются в программу до тех пор, пока не возникла ошибка, в этом случае задается переменнаяexecи функцияerrnoвозвращает -1.execНовые процессы, запущенные exec, наследуют многие свойства исходного процесса. В частности, открытые файловые дескрипторы остаются открытыми в новом процессе, пока не установлен их флаг
(close on exec) (подробную информацию см. в описании системного вызоваFD_CLOEXECв главе 3). Любые открытые в исходном процессе потоки каталогов закрываются.fcntlДублирование образа процесса
Для применения процессов, выполняющих несколько функций одновременно, можно либо использовать потоки, обсуждаемые в главе 12, либо создавать в программе полностью отдельный процесс, как делает
, вместо замещения текущего потока исполнения, как в случае применения функцииinit.execСоздать новый процесс можно с помощью вызова
. Системный вызов дублирует текущий процесс, создавая новый элемент в таблице процессов с множеством атрибутов, таких же как у текущего процесса. Новый процесс почти идентичен исходному, выполняет тот же программный код, но в своем пространстве данных, окружении и со своими файловыми дескрипторами. В комбинации с функциямиforkвызовexec— все, что вам нужно для создания новых процессов.fork<b>#include <sys/types.h></b><b>#include <unistd.h></b><b>pid_t fork(void);</b>Как видно из рис. 11.2, вызов
возвращает в родительский процесс PID нового дочернего процесса. Новый процесс продолжает выполнение так же, как и исходный, за исключением того, что в дочерний процесс вызовforkвозвращает 0. Это позволяет родительскому и дочернему процессам определить, "кто есть кто".forkРис. 11.2
Если вызов
завершается аварийно, он возвращает -1. Обычно это происходит из-за ограничения числа дочерних процессов, которые может иметь родительский процесс (fork), в этом случае переменнойCHILD_MAXбудет присвоено значениеerrno. Если для элемента таблицы процессов недостаточно места или не хватает виртуальной памяти, переменнаяEAGAINполучит значениеerrno.ENOMEMДалее приведен фрагмент типичного программного кода, использующего вызов
:forkpid_t new_pid;new_pid = fork();switch(new_pid) {case -1:/* Ошибка */break;case 0:/* Мы — дочерний процесс */break;default:/* Мы — родительский процесс */break;}Выполните упражнение 11.3.
Упражнение 11.3. Системный вызовforkДавайте рассмотрим простой пример fork1.с:
#include <sys/types.h>#include <unistd.h>#include <stdio.h>#include <stdlib.h>int main() {pid_t pid;char* message;int n;printf("fork program starting\n");pid = fork();switch(pid) {case -1:perror("fork failed");exit(1);case 0:message = "This is the child";n = 5;break;default:message = "This is the parent";n = 3;break;}for (; n > 0; n--) {puts(message);sleep(1);}exit(0);}Эта программа выполняет два процесса. Дочерний процесс создается и выводит пять раз сообщение. Исходный процесс (родитель) выводит сообщение только три раза. Родительский процесс завершается до того, как дочерний процесс выведет все свои сообщения, поэтому в вывод попадает очередное приглашение командной оболочки.
$ <b>./fork1</b>fork program startingThis is the childThis is the parentThis is the parentThis is the childThis is the parentThis is the child$ This is the childThis is the childКак это работает
Когда вызывается
, эта программа делится на два отдельных процесса. Родительский процесс идентифицируется ненулевым возвращаемым изforkзначением и используется для задания количества сообщений, выводимых с интервалом в одну секунду.forkОжидание процесса
Когда вы запускаете дочерний процесс с помощью вызова
, он начинает жить собственной жизнью и выполняется независимо. Иногда вам нужно знать, когда закончился дочерний процесс. Например, в предыдущей программе родительский процесс завершается раньше дочернего, и вы получаете слегка беспорядочный вывод, потому что дочерний процесс продолжает выполняться. Вы можете с помощью системного вызоваforkзаставить родительский процесс дождаться завершения дочернего процесса перед своим продолжением.wait
