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

ЖАНРЫ

Язык программирования Perl
Шрифт:

Потомок PID -1024 работает 2 Потомок PID -1908 работает 2

Предок PID 1504 работает 3 Предок PID 1876 работает 3

Закончился предок PID 1504 Предок PID 1876 ждет завершения -1908

Потомок PID -1024 работает 3 Потомок PID -1908 работает 3

Закончился потомок PID -1024 Закончился потомок PID -1908

Закончился предок PID 1876

Выполнение всей программы заканчивается, когда заканчивается последний порожденный процесс. Ожидание окончания выполнения всех дочерних процессов можно организовать с помощью функции wait, которая возвращает PID

завершившегося подпроцесса и -1, если все процессы-потомки завершили работу.

В Perl есть несколько способов организации взаимодействия процессов при их параллельном выполнении. Один из них - создать программный канал (pipe), который представляет из себя два файловых манипулятора - приемник (reader) и передатчик (writer) - связанных таким образом, что записанные в передатчик данные могут быть прочитаны из приемника. Программный канал создается с помощью функции pipe, которой передаются имена двух файловых манипуляторов: приемника и источника. Один из вариантов взаимодействия процессов через программный канал показан в следующем примере:

use IO::Handle; # подключаем стандартный модуль

pipe(READER, WRITER); # создаем программный канал

WRITER->autoflush(1); # включаем авто-очистку буфера

if ($pid = fork) { # процесс-предок получает PID потомка

close READER; # предок не будет читать из канала

print WRITER "Послано предком (PID $$):\n";

for (my $n = 1; $n <= 5; $n++) { # запись в передатчик

print WRITER "$n ";

}

close WRITER; # закрываем канал и

waitpid $pid, 0; # ждем завершения потомка

}

die "fork не отработал: $!" unless defined $pid;

if (!$pid) { # процесс-потомок получает 0

close WRITER; # предок не будет писать в канал

print "Потомок (PID $$) прочитал:\n";

while (my $line = <READER>) { # чтение из приемника

print "$line";

}

close READER; # канал закрывается

exit; # потомок завершает работу

}

Во время выполнения этого примера в стандартный выходной поток будет выведено следующее:

Потомок (PID -2032) прочитал:

Послано предком (PID 372):

1 2 3 4 5

Если нужно организовать передачу данных в обратном направлении, организуется канал, в котором передатчик будет в процессе-потомке, а приемник - в процессе-предке. Так как с помощью программного канала можно передавать данные только в одном направлении, то при необходимости двустороннего обмена данными между процессами создаются два программных канала на передачу в обоих направлениях.

Кроме программных каналов, процессы могут обмениваться информацией и другими способами: через именованные каналы (FIFO) и разделяемые области памяти, если они поддерживаются операционной системой, с помощью сокетов (что будет рассмотрено в следующей лекции) и при помощи сигналов.

В операционных системах имеется механизм, который может доставлять процессу уведомление о наступлении какого-либо события. Этот механизм основан на так называемых сигналах. Работа с ними происходит следующим образом. В программе может быть определен обработчик того или иного сигнала, который автоматически вызывается, когда ОС доставляет сигнал процессу. Сигналы могут отправляться операционной системой, или один процесс может с помощью ОС послать сигнал другому. Процесс, получивший

сигнал, сам решает, каким образом реагировать на него, - например, он может проигнорировать сигнал. Перечень сигналов, получение которых можно попытаться обработать, находится в специальном хэше с именем %SIG. Поэтому допустимые идентификаторы сигналов можно вывести функцией keys(%SIG). Общеизвестный пример - сигнал прерывания выполнения программы INT, который посылает программе операционная система по нажатию на консоли сочетания клавиш Ctrl+C. Как устанавливать обработчик конкретного сигнала, показано на примере обработки сигнала INT:

# устанавливаем обработчик сигнала INT

$SIG{INT} = \&sig_handler; # ссылка на подпрограмму

# начало основной программы

print "Работаю в поте лица...\n" while (1); # бесконечный цикл

sub sig_handler { # подпрограмма-обработчик сигнала

$SIG{INT} = \&sig_handler; # переустанавливаем обработчик

print "Получен сигнал INT по нажатию Ctrl+C\n";

print "Заканчиваю работу!\n";

exit; # завершение выполнения программы

}

Выполнение примера сопровождается выводом сообщений, подтверждающих обработку поступившего сигнала:

Работаю в поте лица...

Получен сигнал INT по нажатию Ctrl+C

Заканчиваю работу!

Примером реальной программы, выполняющейся в бесконечном цикле, может служить любой сервер, ожидающий запросов от клиентских программ и перечитывающий свой конфигурационный файл после получения определенного сигнала (обычно HUP или USR1). Если необходимо временно игнорировать какой-то сигнал, то соответствующему элементу хэша %SIG присваивается строка 'IGNORE'. Восстановить стандартную обработку сигнала можно, присвоив соответствующему элементу %SIG строку 'DEFAULT'.

Процесс может посылать сигналы самому себе, например, для отслеживания окончания запланированного периода времени (для обработки тайм-аута). В приведенном примере длительная операция прерывается по истечении указанного промежутка времени:

# устанавливаем обработчик сигнала ALRM (будильник)

$SIG{ALRM} = sub { die "Timeout"; }; # анонимная подпрограмма

$timeout = 3600; # определяем величину тайм-аута (сек.)

eval { # блок обработки возможной ошибки

alarm($timeout); # устанавливаем время отправки сигнала

# некая длительная операция:

print "Работаю в поте лица...\n" while (1);

alarm(0); # нормальное завершение операции

};

# в специальной переменной $@ - сообщение об ошибке

if ($@ =~ /Timeout/) { # проверяем причину ошибки

print "Аварийный выход по истечении времени!";

}

Отправка сигнала одним процессом другому также используется в качестве средства взаимодействия процессов. Сигнал отправляется процессу с помощью функции kill, которой передаются два аргумента: номер сигнала и PID процесса. Схему реагирования порожденного процесса на сигналы, получаемые от процесса-предка, можно увидеть на следующем учебном примере:

my $parent = $$; # PID родительского процесса

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