Вызов fork, файлы и данные
Созданный при помощи вызова fork дочерний процесс является почти точной копией родительского. Все переменные в дочернем процессе будут иметь те же самые значения, что и в родительском (единственным исключением являете значение, возвращаемое самим вызовом fork). Так как данные в дочернем процессе являются копией данных в родительском процессе и занимают другое абсолютное положение в памяти, важно понимать, что последующие изменения в одном процессе не будут затрагивать переменные в другом.
Аналогично все файлы, открытые в родительском процессе, также будут открытыми и в потомке; при этом дочерний процесс будет иметь свою копию связанных с каждым файлом дескрипторов. Тем не менее файлы, открытые до вызова fork, остаются тесно связанными в родительском и дочернем процессах. Это обусловлено тем, что указатель чтения-записи для каждого из таких файлов используется совместно родительским и дочерним процессами благодаря тому, что он поддерживается системой и существует не только в самом процессе. Следовательно, если дочерний процесс изменяет положение указателя в файле, то в родительском процессе он также окажется в новом положении. Это поведение демонстрирует следующая короткая программа, в которой использована процедура fatal, приведенная ранее в этой главе, а также новая процедура printpos. Дополнительно введено допущение, что существует файл с именем data длиной не меньше 20 символов (xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx).
(* Программа proc_file -- поведение файлов при ветвлении *)
(* Предположим, что длина файла "data" не менее 20 символов *)
uses linux,stdio;
var
fd:integer;
pid:longint; (* идентификатор процесса *)
buf:array [0..9] of char; (* буфер данных для файла *)
begin
fd := fdopen ('data', Open_RDONLY);
if fd = -1 then
fatal ('Ошибка вызова open ');
fdread (fd, buf, 10); (* переместить вперед указатель файла *)
printpos ('До вызова fork', fd);
(* Создать два процесса *)
pid := fork;
case pid of
1: (* ошибка *)
fatal ('Ошибка вызова fork ');
0: (* потомок *)
begin
printpos ('Дочерний процесс до чтения', fd);
fdread (fd, buf, 10);
printpos ('Дочерний процесс после чтения', fd);
end;
else (* родитель *)
begin
wait(nil);
printpos ('Родительский процесс после ожидания', fd);
end;
end;
end.
Процедура printpos просто выводит текущее положение в файле, а также короткое сообщение. Ее можно реализовать следующим образом:
(* Вывести положение в файле *)
procedure printpos(_string:pchar;filedes:integer);
var
pos:longint;
begin
pos := fdseek (filedes, 0, SEEK_CUR);
if pos=-1 then
fatal ('Ошибка вызова lseek');
writeln(_string,':',pos);
end;
После запуска этого примера получены результаты, которые убедительно подтверждают то, что указатель чтения-записи совместно используется обоими процессами:
До вызова fork:10
Дочерний процесс до чтения:10
Дочерний процесс после чтения:20
Родительский процесс после ожидания:20
Упражнение 5.5. Напишите программу, показывающую, что значения переменных программы в родительском и дочернем процессах первоначально совпадают, но не зависят друг от друга.
Упражнение 5.6. Определите, что происходит в родительском процессе, если дочерний процесс закрывает файл, дескриптор которого он унаследовал после ветвления. Другими словами, останется ли файл открытым в родительском процессе или же будет закрыт?