Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учеб.пос.СП.doc
Скачиваний:
28
Добавлен:
31.03.2015
Размер:
1.33 Mб
Скачать
    1. Системный вызов fork

Данный вызов запускает новый процесс, но не новую программу, где новый процесс – это точная копия старого со всеми его данными:

fork - создает новый процесс

#include <unistd.h>

pid_t fork(void);

/* Возвращает идентификатор дочернего процесса или 0 в случае успеха и -1 в случае ошибки (код ошибки - в переменной errno) */

По завершении fork оба процесса получают от него возвращаемое значение. В зависимости от него реакция потомка и предка может сильно отличаться. Обычно процесс-потомок сразу же вызывает exec для запуска новой программы, а предок может либо подождать завершения процесса-потомка, либо заняться своими делами.

Процесс-потомок получает от вызова fork значение 0, родительский процесс - идентификатор процесса-потомка. Возвращаемое значение -1 свидетельствует об ошибке, но поскольку fork не имеет входных аргументов, то ошибочная ситуация никак не связана с вызывающим процессом. Единственная возможная ошибка – исчерпание системных ресурсов, то есть либо нехватка места в файле подкачки, либо в системе исполняется слишком много процессов.

При создании нового процесса вызовом fork процесс-потомок наследует атрибуты от родителя как результат копирования сегмента системных данных предка. Такого рода наследование позволяет пользователю установить атрибуты в командной оболочке, например, текущий каталог, действующий идентификатор пользователя и приоритет, которые затем будут автоматически передаваться всем запускаемым командам. Для наследования недоступно лишь несколько атрибутов:

  • идентификаторы процессов у потомка и предка будут отличаться;

  • если в рамках родительского процесса было запущено несколько потоков, то потомок унаследует только один, тот который вызвал fork;

  • потомок получает от родителя дубликаты открытых дескрипторов. Они соответствуют одним и тем же файлам. Они совместно используют одни и те же записи в системной таблице файлов, а значит и текущие позиции в файлах у них будут одни и те же. Если потомок изменит ее с помощью lseek, то следующая операция ввода-вывода предка будет производиться с новой текущей позиции. Однако сами файловые дескрипторы у предка и потомка разные. Если потомок закроет свой дескриптор, то соответствующий дескриптор предка по-прежнему останется открытым;

  • процесс-потомок сбрасывает накапливаемые значения времени исполнения в пространстве пользователя и ядра в ноль, потому что был порожден новый процесс.

Рассмотрим пример, демонстрирующий работу системного вызова fork:

void forktest(void)

{

int pid;

printf(“Начало теста \n”, pid);

pid = fork();

printf(“Возвращаемое значение %d\n”, pid);

}

Результат работы:

$ forktest

Начало теста

Возвращаемое значение 76542

Возвращаемое значение 0

$

В данной ситуации предок первым вывел свое сообщение, но это совершенно необязательно. Если очередность исполнения имеет значение, процессы можно синхронизировать. Сделать это можно с помощью каналов или сигналов. Перезапустим пример forktest, перенаправив вывод в файл:

$ forktest > tmp

$ cat tmp

Начало теста

Возвращаемое значение 65875

Начало теста

Возвращаемое значение 0

$

Сообщение «Начало теста» получено дважды. Это произошло из-за буферизации вывода - потомок, вместе со всем остальным, унаследовал от предка и частично заполненный выходной буфер. Когда потомок завершил свою работу, его буфер был, вытолкнут, то же самое произошло и с предком. В предыдущем испытании printf не буферизовала свой вывод, поскольку знала, что устройством стандартного вывода является терминал, который предполагает более интерактивный режим работы.