LINUX.ORG.RU

[perl] прога плодит зомби

 


0

0

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

Проблема в чем, если клиенты присоединились и работают постоянно, 
то никаких зомби нет. но ежели начать постоянно присоединяться 
и отсоединяться после получения например одной строчки - то рано 
или поздно отфорканные серверы становятся зомбями и висят в 
процессах с индексом defunct  

что я делаю не так?

#!/usr/bin/perl -w

use lib '/u/vilfred/bwcsII-unix-0.8/weather/File-Tail-0.99.3/blib/lib/';

use File::Tail;
use IO::Socket;
use Symbol;
use POSIX;

$l = '/u/vilfred/bwcsII-unix-0.8/weather/log.txt';
#логфайл, который надо пробросить в сокет

$PORT=15012;
номер порта сокета

$server = IO::Socket::INET->new( Proto => 'tcp',
   LocalPort => $PORT, Listen => SOMAXCONN,
   Type => SOCK_STREAM, Reuse => 1) or die " making socket: $@";
#поднимаем исходно сервер

$PREFORK =5;
#число предварительно отфорканных серверов

$MAX_CLIENTS_PER_CHILD = 5;
#число клиентов, которые могут подсоединяться к каждому из серверов


%children=();
#хеш детей(номер дитяти => его номер процесса)

$children=0;
#колво детей

make_new_child() for(1 .. $PREFORK);
#цикл поддержания постоянного кол-ва 

$SIG{CHLD}=\&REAPER;
#перехватчик сигнала CHILD

$SIG{INT}=\&HUNTSMAN;
#перехватчик killint

while(1){
  sleep;
  for($i=$children; $i<$PREFORK; $i++){make_new_child()}
}
#цикл поддержания постоянного колва серверов, если клиент 
#отсоединился и в месте с ним умер и сервер, нужен чтобы возродить 
#новый сервер

sub tail_socket{
#смотрим в в хвост файла по аналогии с командой tail -f

  return $fi = File::Tail->new(
    name => $l,
    maxinterval => 0.1,
    adjustafter => 1000000000,
    interval => 4,
    tail => 0)
}

sub make_new_child{
#делаем дитенка сервер

  my $pid;
  my $sigset;
  $sigset=POSIX::SigSet->new(SIGINT);
  sigprocmask(SIG_BLOCK, $sigset) or die "can't block SIGINT for fork: $!\n";
  die "fork: $!" unless defined($pid = fork);
  if($pid){
    sigprocmask(SIG_UNBLOCK, $sigset) or die "can't unblock SIGINT for fork: $!\n";
#блокируем сигналы

    $children{$pid}=1;
    $children++;
    return;
  } else {
    $SIG{INT} = 'DEFAULT';
    $SIG{CHLD}='IGNORE';
#переопределяем сигналы на момент подключения

    sigprocmask(SIG_UNBLOCK, $sigset) or die "can't unblock SIGINT for fork: $!\n";
    for($i=0;  $i<$MAX_CLIENTS_PER_CHILD; $i++){
      $client = $server->accept() or last;
#поключаем клиента или выходим
 
     &tail_socket();
#тайлим сокет

      while(1){
#пока есть подключение - данные из хвоста файла запихиваем клиенту

        print $client $_ while($_=$fi->read);
      }
    } exit;
  }
}

sub HUNTSMAN{
  local($SIG{CHLD})='IGNORE';
  kill 'INT' => keys %children;
  exit;
}

sub REAPER{
  $SIG{CHLD}=\&REAPER;
  my $pid = wait;
  $children--;
  delete $children{$pid};
}

Короче, что я делаю не так, чтобы избежать зомбей при большом 
колве подключений и отключений?? Оно не может убить зомби почемуто вобщем.
☆☆

Обработчик сигнала не обязан вызываться ровно столько раз, сколько 
сигнал был послан процессу. Поэтому в обработчике SIGCHLD 
недостаточно просто wait, нужно в цикле вызывать waitpid:

while (waitpid(-1, NULL, WNOHANG) > 0);

Begemoth ★★★★★
()

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

Begemoth ★★★★★
()
Ответ на: комментарий от Begemoth

> Обработчик сигнала не обязан вызываться ровно столько раз,
> сколько сигнал был послан процессу.


не понял, а зачем тогда он нужен? это же обработчик сигнала однако. я не понимаю. что ты хочешь сказать, т.е. каждый 10-й сигнал надо обрабатывать? или каг?

vilfred ☆☆
() автор топика
Ответ на: комментарий от vilfred

Не или каг, а марш Стивенса читать, у него все расписано.

Begemoth ★★★★★
()
Ответ на: комментарий от vilfred

>не понял, а зачем тогда он нужен?

Чтобы обрабатывать сигналы. Но если приложению одновременно придет несколько одинаковых сигналов (например несколько SIGCHLD), то доставлен будет только один. Это факт, с этим спорить бесполезно, это нужно учитывать при кодинге...

Вам предложили в обработчике сигнала SIGCHLD вызвать waitpid() с флагом WNOHANG, но при этом "$SIG{CHLD}=\&REAPER;" нужно делать последним, а не превым оператором подпрограммы REAPER.

Или разместить цикл вызова waitpid() сразу после sleep, а в REAPER делать только "$SIG{CHLD}=\&REAPER;".

mky ★★★★★
()

Я же говорил, что perl не умер. Более того, он плодит себе подобных!

anonymous
()
Ответ на: комментарий от mky

обработчик сигнала убивает процесс по первому сигналу, какие несколько сигналов? Дочернего процесса уже нет после получения уже первого сигнала. проблема в проге не в этом, а в чем-то другом

vilfred ☆☆
() автор топика
Ответ на: комментарий от anonymous

> Это значит что может быть послано 10 сигналов, а доставлен только один.
> Не или каг, а марш Стивенса читать, у него все расписано.


да хоть двести. важен первый, который убивает. все остальное пусть идет куда хочет и сколько хочет... яне об этом спрашиваю. че он в зомби идет, мож там в это время идет запись в File::Tail...

vilfred ☆☆
() автор топика

prefork сервер для такой задачи вобщем то и не нужен, хватит и простого fork сервера

#!/usr/bin/perl
use warnings;
use strict;
use Socket;

my $port = 8080;

$SIG{CHLD} = 'IGNORE';

socket(my $server, PF_INET, SOCK_STREAM, getprotobyname('tcp'));
setsockopt($server, SOL_SOCKET, SO_REUSEADDR, 1);
bind($server, sockaddr_in($port, INADDR_ANY));
listen($server, SOMAXCONN) || die $!;

while ( accept(my $client, $server) ) {
    my $pid = fork;
    die "Can't fork: $!" unless defined $pid;

    if ($pid) { #parent
        close $client
    } else {    #children
        close $server;
        cli_work($client);
        exit 0
    }
}

sub cli_work {
    my $socket = shift;
    print $socket "Hello\n";
    close $socket
}

PoMbl4
()
Ответ на: комментарий от vilfred

>обработчик сигнала убивает процесс по первому сигналу, какие несколько сигналов? Дочернего процесса уже нет после получения уже первого сигнала. проблема в проге не в этом, а в чем-то другом

>да хоть двести. важен первый, который убивает. все остальное пусть идет куда хочет и сколько хочет... яне об этом спрашиваю. че он в зомби идет, мож там в это время идет запись в File::Tail...

Завязывайте с ацетоном. Перечитайте посты в этом топике и поймите о чем вам говорят.

В UNIX, когда дочерний процесс завершает работу, родителю посылается сигнал SIGCHLD, а дочерний процесс переходит в состояние зомби. И находится в этом состоянии до тех пор, пока родительский процесс не сделает системный вызов wait() (а точнее waitpid()). И пока родительский процесс не вызовит waitpid(), зомби будет в системе. Если это понятно, то идем дальше.

В вашем случае на один SIGCHLD вызывается один wait(). А сигналы могут теряться, то есть может не вызваться обработчик сигналов. Поэтому у вас и плодятся зомби при нагрузке, когда родительскому процессу посылается несколько SIGCHLD одновременно.

А если процесс зависает на записи (File::Tail), то он в состоянии D и не получает сигналы и не может завершить работу и стать зомби.

mky ★★★★★
()
Ответ на: комментарий от PoMbl4

а может по твоему и лучше будет...

vilfred ☆☆
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.