Системное программирование в UNIX средствами Free Pascal

Вызов 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. Определите, что происходит в родительском процессе, если дочерний процесс закрывает файл, дескриптор которого он унаследовал после ветвления. Другими словами, останется ли файл открытым в родительском процессе или же будет закрыт?


Содержание раздела