|
Функция fork() в perl создает параллельный процесс в памяти системы, который отличается от родительского лишь своим идентификатором ($$). Все остальные права и полномочия, как будет показано ниже, наследуются.
Пример:
[code]
#!/usr/bin/perl
# Как работает fork()
$|=1; # запрещаем буферизацию вывода
# Если этого не сделать, то при параллельной работе двух процессов
# в браузер будет не все выводиться
print "Content-type: text/html; charset=windows-1251\n\n";
print "<p>Привет\n";
my $parent=$$; # Зафиксируем свои родительские права
# $$ - идентификатор процесса
print "<p>I am parent $parent\n";
my $child=fork(); # Порождаем потомка
# Далее параллельно работают две программы
# отличия только в идентификаторе процесса $$
# Можно записать так:
# if($child=fork()){выполняется родитель}elsif(undef $child){выполняется потомок}else{die "функция fork не сработала"}
# потомок не может породить своего потомка и этим можно пользоваться
print "<p>pid=$child\n"; # если работает потомок, то $child=0
if($$ == $parent){
# выполняется родитель
print "<p>Parent: pid=$$;($parent)\n";
}else{
# выполняется потомок
print "<p>Child: pid=$$;(my parent $parent)\n"; # Потомок знает своего родителя
# Запускаем для хохмы потомка в бесконечном цикле
# Контролировать его будет родитель
while(100){
sleep(1); # каждую секунду показываем его работу
my $t=time();
print "<p>Child (pid=$$): $t\n";
$N++;if($N>20){last} # выход для страховки
}
}
# Но родитель не дремлет, параллельно работает и следит за потомком
my $t0=time();
while(time() <$t0+10){
sleep(5);
print "<p>The parent does not sleep\n";
}
# 10 секунд прошло, можно убивать потомка
if(kill(0,$child)){ # проверим, может быть он уже сам накрылся (сисадмин его прибил с консоли)
# если жив, убиваем. А то ведь, в бесконечном цикле работает
my $kill=kill_my($child);
print "<p>kill=$kill; pid=$$; child=$child\n";
};
print "<p>I am parent=$parent\n<p>The end\n";
if($^O =~/Win/ && !exists($ENV{REMOTE_ADDR})){<>}; # проверим работу проги с консоли
exit;
sub kill_my{
my ($id)=@_;
my $syst=$^O;
# посылаем сигнал TERM системе на ликвидацию процесса $id
# В windows сигналов нет, но зато есть эмуляция некоторых сигналов
if($syst =~/Win/){return kill(9,$id)}else{return kill("TERM",$id)};
}
[/code]
Результат работы скрипта
[code]
Привет
I am parent 14345
pid=0
Child: pid=14346;(my parent 14345)
pid=14346
Parent: pid=14345;(14345)
Child (pid=14346): 1213073844
Child (pid=14346): 1213073845
Child (pid=14346): 1213073846
Child (pid=14346): 1213073847
The parent does not sleep
Child (pid=14346): 1213073848
Child (pid=14346): 1213073849
Child (pid=14346): 1213073850
Child (pid=14346): 1213073851
Child (pid=14346): 1213073852
The parent does not sleep
kill=1; pid=14345; child=14346
I am parent=14345
The end
[/code]
При написании игрового сервера, этот пример может вам пригодиться.
Дополнение 1
Все-таки страховка в бесконечном дочернем цикле оказалась не лишней
$N++;if($N>20){last}
Дело в том, что со смертью (преждевременной) родительского процесса, дочерний процесс продолжает работать.
[code]
if($$ == $parent){
# выполняется родитель
print "<p>Parent: pid=$$;($parent)\n";
# В блок внесены изменения
print "<p>Parrent is killed\n";
kill_my($parent); #добавил. родитель убивает сам себя
print "<p>Parent is died\n"; # этой сточки мы не должны увидеть, потому что родитель уже мертв
exit; # и для надежности exit
}else{
# Выполняется потомок
[/code]
Результат работы с убитым родителем
[code]
Привет
I am parent 4025
pid=4026
Parent: pid=4025;(4025)
Parent is killed
pid=0
Child: pid=4026;(my parent 4025)
Child (pid=4026): 1213080644
Child (pid=4026): 1213080645
Child (pid=4026): 1213080646
Child (pid=4026): 1213080647
Child (pid=4026): 1213080648
Child (pid=4026): 1213080649
Child (pid=4026): 1213080650
Child (pid=4026): 1213080651
Child (pid=4026): 1213080652
Child (pid=4026): 1213080653
Child (pid=4026): 1213080654
Child (pid=4026): 1213080655
Child (pid=4026): 1213080656
Child (pid=4026): 1213080657
Child (pid=4026): 1213080658
Child (pid=4026): 1213080659
Child (pid=4026): 1213080660
Child (pid=4026): 1213080661
Child (pid=4026): 1213080662
Child (pid=4026): 1213080663
Child (pid=4026): 1213080664
N=21;Last
The parent does not sleep (4026)
[/code]
Последнюю строчку выдал не родитель, а потомок, благополучно завершивший цикл по страховке. Если бы не страховка, то остановить его смог бы только сисадмин, который заодно и оплуху тебе влепил бы за разбазаривание системных ресурсов. Во как!
дополнение 2
Проверим, можно ли из дочернего процесса убить родителя.
Вносим измнения в код
[code]
# выполняется потомок
print "<p>Child: pid=$$;(my parent $parent)\n"; # Потомок знает своего родителя
# Запускаем для хохмы потомка в бесконечном цикле
# Контролировать его будет родитель
my $kill=kill_my($parent); # потомок убивает родителя
print "<p>Parent is killed ($kill)\n";
while(1){
sleep(1);
....
[/code]
Результат
[code]
Привет
I am parent 23678
pid=23679
Parent: pid=23678;(23678)
pid=0
Child: pid=23679;(my parent 23678)
Parent is killed (1)
Child (pid=23679): 1213081467
Child (pid=23679): 1213081468
....
Child (pid=23679): 1213081469
Child (pid=23679): 1213081487
N=21;Last
The parent does not sleep (23679)
[/code]
Потомок убивает родителя и спокойно работает дальше.
Ну дела...
Кстати, если то же самое запустить в винде, то там со смертью родительского процесса, умирают и все дочерние. По-моему, я где-то читал, что так и должно быть. А на реальном Unix-сервере процессы живут независимо друг от друга. М-да.
Дополнение 3
Проверим, можно ли из дочернего процесса создать новый дочерний процесс.
Вносим изменения в код
[code]
}else{
# выполняется потомок
print "<p>Child: pid=$$;(my parent $parent)\n"; # Потомок знает своего родителя
# Запускаем для хохмы потомка в бесконечном цикле
# Контролировать его будет родитель
my $kill=kill_my($parent); # убиваем родителя
print "<p>Parent:$parent is killed ($kill)\n";
my $new_pid=fork(); # создаем нового потомка
print "<p>The child:$$ born child ($new_pid)\n"; # показываем его идентификатор
while(1){
sleep(1); # каждые 2 секунды показываем его работу
my $t=time();
print "<p>Child (pid=$$): $t\n";
$N++;if($N>20){print "<p>N=$N;Last\n";last}
}
}
[/code]
Результат
[code]
Привет
I am parent 22349
pid=22351
pid=0
Parent: pid=22349;(22349)
Child: pid=22351;(my parent 22349)
Parent:22349 is killed (1) - родитель УБИТ
The child:22351 born child (22352) - потомок 22351 создает потомка 22352
The child:22352 born child (0) - а этот потомок-внук не может создать нового
Дело в том, что он не может создать нового только в тот момент, когда его самого только что создали. Но в новом fork-е он вполне успешно сделает это.
Child (pid=22351): 1213085644
Child (pid=22352): 1213085644
Child (pid=22351): 1213085645
Child (pid=22352): 1213085645
Child (pid=22351): 1213085646
Child (pid=22352): 1213085646
Child (pid=22351): 1213085647
.. Сын и внук вполне нормально существуют оба при мертвом дедушке
Child (pid=22352): 1213085664
Child (pid=22351): 1213085665
Child (pid=22352): 1213085665
N=21;Last:22351 - по страховке завершился сын
N=21;Last:22352 - по страховке завершился внук
The parent does not sleep (22351)
The parent does not sleep (22352)
[/code]
Такие вот дела... Эдак можно плодить потомков из потомков до бесконечности...
Резюме
Все порожденные процессы могут создавать новые процессы до бесконечности.
Смерть родителя никак не влияет на жизнеспособность потомка.
К примеру такой цикл может обрушить сервер
[code]
while(1){
# на практике не пробовал и вам не советую
slip(1);
fork();
}
[/code]
Каждую секунду будет рождаться новый процесс с новым идентификатором. Сисадмин от этого точно озвереет и что он сделает с вами не могу даже представить.
В общем, все надо попробовать самому, иначе жизнь будет пресной и скучной.
|