| Содержание | Предисловие | В |
Глава 14В этой главе:
Использование обратных кавычек Использование процессов как дескрипторов файлов Использование функции fork Сводка операций, проводимых над процессами Передача и прием сигналов Упражнения Управление процессамиИспользование функций system u ехесКогда вы из командной строки shell задаєте выполнение какой-либо команды, он обычно создает новый процесе. Зтот новый процАналогичным образом Perl-программа в состоянии запускать новые процессы и может делать зто, как и большинство других операций, несколь-кими способами. Самий простой способ запуска нового процесса — использовать для зтого функцию system. В простейшей форме зта фsystem("date"); Здесь мы не проверяем возвращаемое значение, но неудачный исход выполнения команд н date вряд ли возможен.Куда идет результат зтой команди? Откуда поступают исходные данные, если они нужны команде? Хорошие вопросы, и ответы на них позволят узнать, чем отличаются различные способы создания процессов. * В данном случае shell фактически не используется, поскольку Рсгі сам выполняет операцийТри стандартних файла для функции system (стандартний ввод, стандартний внвод и стандартний вивод ошибок) наследуются от Perl-процsystem("date >right_now") && die "cannot create right_now"; На зтот раз ми не только посилаєм результат команди date в файл, внполняя переадресацию в shell,Аргументом функции system может бить все, что пригодно для передачи в /Ып/sh, позтому можно задавать срВот пример задания команд date и who в$where = "who_out." .++$і; t получить новое имя файла system "(date; who) >$where &";В зтом случае функция system возвращает код выхода shell и покаПомимо стандартних дескрипторов файлов, порожденний процесе на-следует от родительского процесса много других вещей. Зто текущее значение, заданное командой umask, теКроме того, порожденний процесе наследует все переменнне среды. Зти переменные обычно изменяются командой csh setenv или же соответствующим В Perl предусмотрена возможность проверки и изменения текущих пере-менных среды посредством специального хеша, который называется %env. Вот простая программа, которая работает, как printenv:foreach $key (sort keys %ENV) ( print "$key = $ENV($key)\n"; > . , Обратите внимание: знак равенства здесь — зто не символ операции присваивания, а просто текстовый символ, с помощью которого функцВот фрагмент программы, с помощью которого значение переменной path изменяется таким образом, чтобы поиск команды$oldPATH = $ENV{ "PATH"); # сохранить предыдущий путь $ENV("PATH") = "/bin:/usr/bin:/usr/ucb"; # ввестиКак много текста придется набирать! Гораздо быстрее будет просто установить локальнеє значение для зтого злемента хеша. Несмотря на наличие некоторых недостатков, операция local может делать одну вещь, которая не под силу операции ту: она способна присваи-вать временное значение одному злемен{ local $ENV{"PATH"> " "/bin:/usr/bin:/usr/ucb"; ! systemC'grep fred bedrock >output"); > Функция system может принимать не один аргумент, а список аргумен-тов. В зтом случае Perl не передает список аргументов вsystem "grep 'fred flintstone' buffaloes"; # с использованием shell system "grep","fred flintstone","buffaloes"; # без исполПрименение в функции system списка, а не одной строки, зкономит также один процесе shell,Вот еще один пример зквивалентных форм:Ocfiles = ("fred.c","barney.c"); # что компилировать Soptions = ("-DHARD","-DGRANITE"); # опцииИспользование обратных кавычек Еще один способ запуска процесса — заключить командную строку для /Ып/sh в обратные кавычки. Как и в$now = "the time is now".'date'; # получает текст и датуЗначение переменной $now теперь представляет собой текст the time is nowthe time is now Fri Aug 13 23:59:59 PDT 1996 Если взятая в обратные кавычки команда используется не в скалярном, а в списочном контексте, то возвращается список строкових значений, каждое из которых представляет собой строку (оканчивающую merlyn tty42 Dec 7 19:41 fred ttylA Aug 31 07:02 barney ttylF Sep 1 09:22 Вот как можно получить зтот результат в списочном контексте: foreach $_ ('who') ( # один раз для каждой строки текста из who <$who,$where,$when) = /(\S+)\s+(\S+)\s+(.*)/;print "$who on $where at $when\n"; } * Или символом, которьш у вас занесен в переменную $/.При каждом выполнении зтого цикла используется одна строка виход-ных данных команди who, потому что взятая в обратные кавичСтандартний ввод и стандартний вывод ошибок команди, взятой в обратные кавички, наследуются от Perl-процесса*. Зто значит, что обычно стандартний вывод таких команд ви можете получить как значение строки, заключенной в обратные кавички. die "rm spoke!" if 'rm fred 2>&1'; Здесь Perl-процесе завершается, если rm посылает какое-нибудь сообще-ние —Использование процессов как дескрипторов файлов Следующий способ запуска процесса — создание процесса, которий вигля-дит как дескриптор файла (аналогично библиотечной подopen(WHOPROC, "who 1"); # открнть who для чтенияОбратите внимание на вертикальную черту справа от who. Зта черта информирует Perl* На самом деле все не так просто. См. соответствующий ответ в разделе 8 сборника часто задаваемых вопросов** Ho не одновременно. Примеры двунаправленной связи приведены в главе 6 книги Programming Perlчтения, что означает возможность вьшолнения над файлом всех обычных операций ввода-вывода. Вот как можно прочитать данные из команды whoАналогичннм образом для запуска команды, которой необходимы вход-ные данные, мы можем открыть дескриптор файла процесса для записи, поставив вертикальную черту слева от команды, например: open(LPR,"|Ipr -Psiatewriter") ; print LPR Srockreport; close(LPR) ; В атом случае после открытия lpr мы записываем в него данные и закриваєм данный файл. Открьггие процесса с дескриптором файла позвОткрытие процесса для записи предполагает, что стандартний ввод команды будет получен из дескриптора файла. Стандартний вывод и стандартний вивод ошибок используются зтим процессом совместно с Perl.open(LPR,"|Ipr -Psiatewriter >/dev/null 2>&1"); С помощью операций >/dev/null обеспечивается отбраснвание стандартного вивода путем переадресации его на нулевое устройство. ОпМожно даже обьединить все зти фрагменти программи и в результате получить отчет обо всех зарегистрированных пользователях, кроме Фреда: open (WHO,"who[ ") ; open (LPR,"|Ipr - Psiatewriter"); while (<WHO>) ( unless (/fred/) { # не показывать имя Фред print LPR $_:> } close WHO; close LPR; Считивая из дескриптора who по одной строке, зтот фрагмент кода выводит в дескриптор lprВовсе не обязательно указывать в команде open только по одной команде за один прием. В ней можно задать сразу весь конвейєр. Наприopen(WHOPR, "Is I tail -r I"); Использование функции forkЕще один способ создания нового процесса — клонирование текущего Perl-процесса с помощью UNIX-функцииif (!defined($child_pid = fork()) { die "cannot fork: $!"; } elsif ($pid) { 4s я — родительский процесе } else (# я — порожденннй процесе >Чтоби максимально зффективно использовать зтот клон, нам нужно изучить еще несколько функции, которьге восьма похожи на своих UNIX-тезок: зто функции wait, exitСамая простая из них — функция ехес. Зто почти то же самое, что и функция system, за тем исключением, что вмесехес "date";заменяет текущую Perl-программу командой date, направляя результат зтой команди на стандартний вивод Perl-программи. ПослеВсе зто можно рассматривать и по-другому: функция system похожа на комбинацию функции fork c функцией ехес, на# МЕТОД І... использование system:system("date"); t МЕТОД 2... использование fork/exec:unless (fork) ( # fork exec ("date") ; t порожденный процесе становится командой date} Использовать fork и exec таким способом —if (!defined($kidpid = fork()) ( # fork возвратила undef, т.е. неудача ” elsif ($pid =" 0) {# fork возвратила О, позтому данная ветвь — порожденный процесе# если exec терпит неудачу, перейти к следующему оператору} else { # fork возвратила не 0 и не undef,# позтому данная ветвь — родительский процесе waitpid($k} Если все зто кажется вам слишком сложным, изучите системные вызовы fork(2) и ехес(2),Функция exit обеспечивает немедленньш выход из текущего Perl-про-цесса. Она используется для прерывания Perl-программы где-нибудьunless (defined ($pid = fork)) ( die "cannot fork: $!"; } unless ($pid) ( unlink </tmp/badrock.*>; # ) # родительский процесе продолжается здесь waitpid($pid, 0); # после уничтожения порожденного процесса нужно все убратьБез использования функций exit порожденный процесе продолжал бы вьшолнять Perl-код (со строки "# родительский процесе продолжаетсяФункция exit может иметь необязательный параметр, служащий числовим кодом выхода, который воспринимается родительским процессом. По умолчанию выход производится с нулевым коСводка операций, проводимых над процессами Операции, служащие для запуска процессов, перечислены в таблице 14.1. Таблица 14.1.
|
Операция
|
Стандартний ввод
|
Стандартний вывод
|
Стандартний вывод ошибок
|
Нужно ли ожидать завершення процесса
|
System()
|
Наследуется
|
Наследуется
|
Наследуется
|
Да
|
|
от программы
|
от программы
|
от программы
|
|
Строка в обратных
|
Наследуется от программы
|
Принимается как строковое
|
Наследуется от программы
|
Да
|
кавычках
|
|
значение
|
|
|
Запуск
|
Соединен с
|
Наследуется
|
Наследуется
|
Только во
|
процесса как деск
|
дескриптором файла
|
от программы
|
от программы
|
время вы-полнения
|
риптора файла для
|
|
|
|
close ()
|
вывода при
|
|
|
|
|
помощи
|
|
|
|
|
команди
|
|
|
|
|
open()
|
|
|
|
|
Запуск
|
Наследуется
|
Соединен с
|
Наследуется
|
Только во
|
процесса как деск
|
от программы
|
дескриптором файла
|
от программы
|
время вы-полнения
|
риптора файла для
|
|
|
|
close ()
|
ввода при
|
|
|
|
|
помощи
|
|
|
|
|
команди
|
|
|
|
|
open()
|
|
|
|
|
fork,
|
Выбирается
|
Выбирается
|
Выбирается
|
Выбирается
|
ехес,
|
пользователем
|
пользователем
|
пользователем
|
пользователем
|
wait,
|
|
|
|
|
waitpid
|
|
|
|
|
Самый простой способ создать процесе
— использовать для зтого функ-цию system.Простой способ получить асинхронний процесе (процесе, который по-зволяет продолжать выполнение Perl-программы до своего завершення) — открыть команду как дескриптор файла сСамый гибкий способ запустить процесе
— заставить программу вызвать функции fork, ехес и wПередача и прием сигналов
Один из вариантов организации межпроцессного взаимодействия осно-ван на передаче и приеме сигналов. Сигнал
— зто одноразряОтвет на сигнал называется действием сигнала. Фиксированные сигналы выполняют определенные действия по умолчанию, например, осуществляют прерывание или приостановку процесса. Остальные сигналы по умолчанию
*
Полезно таюке знать о формах типа open(STDERR, ">&stdout") , используемых для точной настройки дескполностью игнорируются. Почти для любого сигнала действие по умолчанию может быть переопределено, с тем чтобы данный сигнал либо игнорировал-ся, либо перехватывался (с автоматическим вызовом ук
Все зто аналогично тому, что делается и в других языках программиро-вания, но сейчас излагаемый материал приобретет специфический Perl-от-тенок. Когда Perl-процесе перехватывает сигнал, асинхронне и автоматиче-ски вызывается указанная в
Обычно подпрограмма-обработчик сигнала делает одно из двух: преры-вает программу, выполнив "очистку", или устанавливает какой-то флаг (например, глобальную переменную), которую данная программа затем проверяет*.
Для того чтобы зарегистрировать подпрограммы-обработчики сигналов в
Perl, нужно знать имена сигналов. После регистрации обработчика сигнала Perl$SIG{'INT'} ” 'my_sigint_catcher';
Но нам понадобится также определение зтой подпрограммы. Вот пример простого определения:
sub my_sigint_catcher (
$saw_sigint = 1;
# установить флаг }Данный перехватчик сигналов устанавливает глобальную переменную и сразу же возвращает управление. Выполнение программы продолжается с той позиции, в которой оно было прервано. Обычно сначала об
$saw_sigint = 0;
# очистить флаг $SIG{'INT') = 'my_sigint_catcher'; # зарегистрировать перехватчикforeach (@huge_array) (
#
что-нибудь сделатьt
еще что-нибудь сделать# и еще что-нибудь
if ($saw_sigint) ( # прерывание нужно?t
здесь "очистка" last;} } $SIG('INT'} = 'DEFAULT'; #
восстановить действие по умолчанию*
Попытка вьшолнения действий более сложных, чем вышеописанные, вероятнее всего, запутает ситуацию; большинство внутренних механизмов PerlОсобенность использования сигнала в данном фрагменте программы состоит том, что значение флага проверяется в важнейших точках процесса вычисления и используется для преждевременного выхода из ц
Один из способов генерирования сигнала
sigint — заставить пользователя нажать на клавиатуре терминала соответствующие прерыванию клавиши (на-пример, [Ctrkill(2,234,237);
# послать SIGINT вБолее подробно вопросы обработки сигналов описаны в главе
6 книги Programming PerlУпражнения
Ответы к упражнениям см. в приложении А.
Напишите программу, которая получает результат команды
date и вычис-ляет текущий день недели. Если день недели —Напишите программу, которая получает все реальнне имена пользователей из файла
/etc/passwd, а затем трансформирует результасоздайте хеш, где ключ
— регистрационное имя, а значение — реальное имя.) Попробуйте вьшолнить зту задачу с исМодифицируйте предыдущую программу так, чтобы ее результат автомати-чески поступал на принтер. (Если у вас нетдоступа к принтеру, то, вероятно, вы можете послать самому себе сообщение злектронной почты.)
Предположим, функция
mkdir перестала работать. Напишите подпро-грамму, которая не использует mkdir, а вызываетРасширьте программу из предыдущего упражнения так, чтобы в ней устанавливались права доступа (с помощью функции
chmod).| Содержание | Предисловие | В |