Скачиваний:
56
Добавлен:
08.01.2014
Размер:
2.6 Mб
Скачать

Пример docommand

Модуль stdio предоставляет библиотечную процедуру runshell, которая позволяет выполнить в программе команду оболочки. Для примера создадим упрощенную версию этой процедуры docommand, используя вызовы fork и ехес. В качестве посредника вызовем стандартную оболочку (заданную именем /bin/sh), а не будем пытаться выполнять программу напрямую. Это позволит программе docommand воспользоваться преимуществами, предоставляемыми оболочкой, например, раскрытием шаблонов имен файлов. Задание параметра -с вызова оболочки определяет, что команды передаются не со стандартного ввода, а берутся из следующего строчного аргумента.

(* Программа docommand -- запуск команды оболочки, первая версия *)

uses linux,stdio;

function docommand(command:pchar):integer;

var

pid:longint;

begin

pid := fork;

if pid < 0 then

begin

docommand:=-1;

exit;

end;

if pid = 0 then (* дочерний процесс *)

begin

linuxexecl('/bin/sh', 'sh', ['-c', command, nil]);

perror ('execl');

halt(1);

end;

(* Код родительского процесса *)

(* Ожидание возврата из дочернего процесса *)

wait(nil);

docommand:=0;

end;

begin

docommand('ls -l | wc -l');

end.

Следует сказать, что это только первое приближение к настоящей библиотечной процедуре runshell. Например, если конечный пользователь программы нажмет клавишу прерывания во время выполнения команды оболочки, то и вызывающая программа, и команда остановятся. Существуют способы обойти это ограничение, которые будут рассмотрены в следующей главе.

5.5. Наследование данных и дескрипторы файлов

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

Соседние файлы в папке Полищук, Семериков. Системное программирование в UNIX средствами Free Pascal