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-)
Поделиться с друзьями: