Опубликован: 17.08.2006 | Доступ: свободный | Студентов: 5312 / 680 | Оценка: 4.49 / 3.94 | Длительность: 20:58:00
ISBN: 978-5-9556-0078-9
Лекция 16:

Взаимодействие процессов

< Лекция 15 || Лекция 16: 1234 || Лекция 17 >

Замена текущего процесса

Иногда требуется организовать выполнение программы таким образом: вначале запускается загрузчик, который, в зависимости от условий, заданных в конфигурации программы, запускает вместо себя основную программу. Этот подход можно реализовать с помощью функции exec, которая заменяет работающую программу на указанную. Так можно запускать не только Perl-программы. Этот прием можно проиллюстрировать таким примером:

print "Выполняется загрузчик: $0, PID:$$\n";
# заменить текущую программу на указанную
my $program = $ARGV[0]; # имя программы в 1-м аргументе
print "Запускается программа: $program\n";
exec 'perl', $program or die; # запуск программы
print "Это сообщение никогда не напечатается!\n";

При запуске этого примера с параметром 'proc_executed.pl' будут выведены такие сообщения:

Выполняется загрузчик: proc_exec.pl, PID:652
Запускается программа: proc_executed.pl
Выполняется программа: proc_executed.pl, PID:1872

Параллельное выполнение процессов

Для организации параллельного выполнения процессов в Perl используется функция fork ("разветвить"). В результате ее работы создается копия выполняющегося процесса, которая тоже запускается на выполнение. Для этого в операционных системах семейства Unix происходит обращение к системному вызову fork. В других операционных системах работа функции fork() организуется исполняющей системой Perl. Функция fork() в родительском процессе возвращает PID дочернего процесса, число 0 - в дочернем процессе и неопределенное значение при невозможности запустить параллельный процесс. Это значение проверяется в программе, чтобы организовать выполнение различных действий в процессе-предке и процессе-потомке. Как это делается, показано на следующем схематичном примере (где оба процесса в цикле выводят числа, но с разными задержками):

my $pid = fork(); # 'разветвить' текущий процесс
# fork вернет 0 в потомке и PID потомка в процессе-предке
die "fork не отработал: $!" unless defined $pid;
unless ($pid) {              # процесс-потомок
   print "Начался потомок PID $$\n";
   for (1..3) {             
      print "Потомок PID $$ работает $_\n";
      sleep 2;           # 'заснуть' на 2 секунды
   }
   print "Закончился потомок PID $$\n";
   exit;
} 
if ($pid) {              # процесс-предок
   print "Начался предок PID $$\n";
   for (1..3) {              
      print "Предок PID $$ работает $_\n";
      sleep 1;           # 'заснуть' на 1 секунду
   }
   # возможно, здесь нужно ждать завершения потомка:
   # print "Предок PID $$ ждет завершения $pid\n";
   # waitpid $pid, 0;
   print "Закончился предок PID $$\n";
}

По сообщениям, выводимым при выполнении этого примера, видно, что родительский и порожденный процессы выполняются параллельно. Для того чтобы организовать в родительском процессе ожидание завершения дочернего процесса, применяется функция waitpid(), которой передается PID процесса-потомка (а также, возможно, дополнительные параметры). По выдаваемым сообщениям сравните два варианта выполнения приведенной выше программы - без ожидания завершения дочернего процесса и с ожиданием завершения процесса-потомка (для этого нужно раскомментарить вызов функции waitpid ):

Без ожидания потомка		С ожиданием потомка по waitpid()
----------------------------	--------------------------------
Начался потомок PID -1024	Начался потомок PID -1908
Потомок PID -1024 работает 1	Потомок PID -1908 работает 1
Начался предок PID 1504		Начался предок PID 1876
Предок PID 1504 работает 1	Предок PID 1876 работает 1
Предок PID 1504 работает 2	Предок PID 1876 работает 2
Потомок 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) и разделяемые области памяти, если они поддерживаются операционной системой, с помощью сокетов (что будет рассмотрено в следующей лекции) и при помощи сигналов.

< Лекция 15 || Лекция 16: 1234 || Лекция 17 >
Галина Башкирова
Галина Башкирова

Здравствуйте, недавно закончила курс по проф веб программиованию, мне прислали методические указания с примерами тем, однако темы там для специальности 

Системный администратор информационно-коммуникационных» систем.
Мне нужно самой найти тему? или делать по высланным темам

 

Константин Моренко
Константин Моренко
Марина Дайнеко
Марина Дайнеко
Россия, Moscow, Nope, 2008
Сергей Пантелеев
Сергей Пантелеев
Россия, Москва