Чтение онлайн

ЖАНРЫ

UNIX — универсальная среда программирования
Шрифт:
Создание процесса низкого уровня:
execlp
и
execvp

Самая важная операция - выполнение другой программы без возврата с помощью системного вызова

execlp
. Например, чтобы напечатать дату и выполнить тем самым последнее действие запущенной программы, используют

execlp("date", "date", (char*)0);

Первый аргумент

execlp
есть имя файла команды;
execlp
выбирает путь поиска (т.е.
$PATH
) из вашего окружения и выполняет такой же поиск, как
shell
. Второй и последующие аргументы — это имена и аргументы команд; для
новой программы они становятся массивом
argv
. Конец списка отмечен аргументом 0. (См. справочное руководство по
exec(2)
, и вы поймете конструкцию
execlp
.)

Вызов

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

execlp("date", "date", (char*)0);

fprintf(stderr, "Не удалось выполнить 'date'\n");

exit(1);

Если число аргументов вам заранее не известно, полезно применить

execvp
(вариант
execlp
). Вызов выглядит так:

execvp(filename, argp);

где

argp
означает массив указателей к аргументам (таким, как
argv
). Последним в массиве должен быть указатель
NULL
, так что
execvp
может отметить конец списка. Как и для
execlp
,
filename
— это файл, в котором находится программа,
argp
— массив
argv
для новой программы, a
argp[0]
— имя программы.

Ни одна из перечисленных выше программ не обеспечивает расширения в списке аргументов метасимволов типа

<
,
>
,
*
, кавычки и т.п. Если они вам нужны, воспользуйтесь
execlp
и вызовите
/bin/sh
из
shell
, которая выполнит эту работу. Сконструируйте строку
commandline
, содержащую полную команду, как если бы она была напечатана на терминале, например:

execlp("/bin/sh/", "sh", "-с", commandline, (char*)0);

Аргумент

– с
предписывает трактовать следующий аргумент как целую командную строку.

В качестве иллюстрации

exec
рассмотрим программу
waitfile
. Команда

$ waitfile filename [command]

периодически проверяет поименованный файл. Если он не менялся после последней проверки, выполняется

command
. В том случае, когда команда не указана, файл копируется в стандартный выходной поток. С помощью
waitfile
мы контролируем работу
troff
, как в

$ waitfile troff .out echo troff done &

Программа

waitfile
использует
fstat
, чтобы выявить время последнего изменения файла.

/* waitfile: wait until file stops changing */

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

char *progname;

main(argc, argv)

 int argc;

 char *argv[];

{

 int fd;

 struct stat stbuf;

 time_t old_time = 0;

 progname = argv[0];

 if (argc < 2)

error("Usage: %s filename [cmd]", progname);

 if ((fd = open(argv[1], 0)) == -1)

error("can't open %s", argv[1]);

 fstat(fd, &stbuf);

 while(stbuf.st_mtime != old_time) {

old_time = stbuf.st_mtime;

sleep(60);

fstat(fd, &stbuf);

 }

 if (argc == 2) { /* copy file */

execlp("cat", "cat", argv[1], (char*)0);

error("can't execute cat %s", argv[1]);

 } else { /* run process */

execvp(argv[2], &argv[2]);

error("can't execute %s", argv[2]);

 }

 exit(0);

}

Мы

рассмотрели пример работы как
execlp
, так и
execvp
. Эта программа выбрана в качестве иллюстрации, поскольку она весьма полезна, но возможны и другие варианты. Так,
waitfile
могла бы просто завершиться по окончании изменения файла.

Упражнение 7.17

Модифицируйте

watchfile
(упр. 7.12) так, чтобы она имела то же свойство, что и
waitfile
: в отсутствие
command
копируется файл, в противном случае выполняется команда. Могли бы
watchfile
и
waitfile
разделять исходную программу? Подсказка:
argv[0]
.

Управление процессами:
fork
и
wait

Следующий шаг — вновь получить управление после запуска программы с помощью

execlp
и
execvp
. Так как эти программы просто "перекрывают" старую программу новой, для сохранения старой требуется сначала разбить ее на две копии. Одна из копий может быть перекрыта, в то время как другая ждет новую, перекрывающую ее программу, чтобы завершиться. Разбиение выполняется с помощью системного вызова
fork
:

proc_id = fork;

Программа разбивается на две копии, каждая из которых продолжает работать. Они отличаются лишь значением, возвращаемым

fork
, — номером процесса
process-id
. В первом процессе (потомке)
proc_id
равен нулю, во втором (родительском)
proc_id
есть номер процесса-потомка. Итак, вызвать другую программу и вернуться можно следующим образом:

if (fork == 0)

 execlp("/bin/sh", "sh", "-с", commandline, (char*)0);

Фактически этого достаточно, за исключением обработки ошибок.

Fork
делает две копии программы. В процессе-потомке
fork
возвращает нуль, так что он вызывает
execlp
, которая выполняет
commandline
и затем завершается. В родительском процессе
fork
возвращает не нуль, поэтому
execlp
пропускается. (При наличии ошибки
fork
возвращает -1-)

Поделиться с друзьями: