Идентификатор процесса
Как было уже отмечено в начале этого раздела, вызов fork не имеет аргументов и возвращает идентификатор процесса
pid типа longint. Пример вызова:
uses linux;
var
pid:longint;
pid := fork;
Родитель и потомок отличаются значением переменной pid. В родительском процессе значение переменной pid будет ненулевым положительным числом, для потомка же оно равно нулю. Так как возвращаемые в родительском и дочернем процессе значения различаются, то программист может задавать различные действия для двух процессов.
Значение, возвращаемое родительскому процессу в переменной pid, называется идентификатором процесса (process-id) дочернего процесса. Это число идентифицирует процесс в системе аналогично идентификатору пользователя. Поскольку все процессы порождаются при помощи вызова fork, то каждый процесс UNIX имеет уникальный идентификатор процесса.
Следующая короткая программа более наглядно показывает работу вызова fork и использование идентификатора процесса:
(* Программа spawn - демонстрация вызова fork *)
uses linux;
var
pid:longint; (* process-id в родительском процессе *)
begin
writeln ('Пока всего один процесс');
writeln ('Вызов fork...');
pid := fork; (* создание нового процесса *)
if pid = 0 then
writeln ('Дочерний процесс')
else if (pid > 0) then
writeln ('Родительский процесс, pid потомка ', pid)
else
writeln ('Ошибка вызова fork, потомок не создан');
end.
Оператор if, следующий за вызовом fork, имеет три ветви. Первая определяет дочерний процесс, соответствующий нулевому значению переменной pid. Вторая задает действия для родительского процесса, соответствуя положительному значению переменной pid. Третья ветвь неявно соответствует отрицательному, а на самом деле равному -1, значению переменной pid, которое возвращается, если вызову fork не удается создать дочерний процесс. Это может означать, что вызывающий процесс попытался нарушить одно из двух ограничений; первое из них – системное ограничение на число процессов; второе ограничивает число процессов, одновременно выполняющихся и запущенных одним пользователем. В обоих случаях переменная linuxerror содержит код ошибки Sys_EAGAIN. Обратите также внимание на то, что поскольку оба процесса, созданных программой, будут выполняться одновременно без синхронизации, то нет гарантии, что вывод родительского и дочернего процессов не будет смешиваться.
Перед тем как продолжить, стоит обсудить, зачем нужен вызов fork, поскольку сам по себе он может показаться бессмысленным. Существенный момент заключается в том, что вызов fork обретает ценность в сочетании с другими средствами
UNIX. Например, возможно, что родительский и дочерний процессы будут выполнять различные, но связанные задачи, организуя совместную работу при помощи одного из механизмов межпроцессного взаимодействия, такого как сигналы или каналы (описываемые в следующих главах). Другим средством, часто используемым совместно с вызовом fork, является системный вызов ехес, позволяющий выполнять другие программы, и который будет рассмотрен в следующем разделе.
Упражнение 5.1. Программа может осуществлять вызов fork несколько раз. Аналогично каждый дочерний процесс может вызывать fork, порождая своих потомков. Чтобы доказать это, напишите программу, которая создает два подпроцесса, а они, в свою очередь, – свой подпроцесс. После каждого вызова fork каждый родительский процесс должен использовать функцию writeln для вывода идентификаторов своих дочерних процессов.