🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: pl.comp.lang.perl
13 messages
13 total messages Started by Paweł Thu, 03 Dec 2009 18:25
wątki
#51677
Author: Paweł
Date: Thu, 03 Dec 2009 18:25
52 lines
2009 bytes
Witam !


W przykładach dotyczących wątków można znaleźć zwykle coś takiego:

$SIG{'CHLD'}=sub { while(waitpid(-1,WNOHANG)>0){} };

defined (my $child=fork()) or die "Błąd wywołania fork():$!";
if ($child==0)
	{....
	exit 0;}

Interesuje mnie pierwsza lina.
W opisie podają, ze wykonuję sie ona po zakończeniu procesu dziecka.
Nie bardzo mogę zrozumieć w jakim celu.
Jeśli proces się zakończy to zwalniane są zasoby i nie widzę sensu aby 
coś jeszcze robić. Jednak jeśli ta linia wykonuje się przed startem 
procesu dziecka to jej wywołanie jest dla mnie logiczne. Zapobiegnie ew. 
tworzeniu się kolejnych wątków. Czy ktoś może wyjaśnić jak to jest w 
rzeczywistości ?





Załóżmy, że program główny składa się z jednej pętli okresowo 
wywoływanej. Chcę w tej pętli utworzyć nowy proces. Jednak tylko w 
sytuacji gdy poprzedni się zakończył. Czy można zrobić to w ten sposób:


my $t=0;
while (1)
{
...

if ($t==0)
	{
		defined (my $child=fork()) or die "Błąd wywołania fork():$!";
	if ($child==0)
		{
		$t=1;
		...

		...
		$t=0;
		exit 0;}
	}

{


Paweł
Re: wątki
#51693
Author: Stachu 'Dozzie' K.
Date: Thu, 03 Dec 2009 19:04
46 lines
2275 bytes
On 03.12.2009, Paweł wrote:
> Witam !
>
>
> W przykładach dotyczących wątków można znaleźć zwykle coś takiego:
>
> $SIG{'CHLD'}=sub { while(waitpid(-1,WNOHANG)>0){} };
>
> defined (my $child=fork()) or die "Błąd wywołania fork():$!";
> if ($child==0)
> 	{....
> 	exit 0;}
           ^
Mógłbyś ten fragment kodu jakoś sformatować? Na przykład kończyć nawiasy
klamrowe w przewidywalnych miejscach? Bo później się bardzo źle czyta
dłuższy kod.

> Interesuje mnie pierwsza lina.
> W opisie podają, ze wykonuję sie ona po zakończeniu procesu dziecka.
> Nie bardzo mogę zrozumieć w jakim celu.
> Jeśli proces się zakończy to zwalniane są zasoby i nie widzę sensu aby 
> coś jeszcze robić.

Doczyta o procesach zombie, to będzie wiedział że jeszcze nie wszystko
jest zwalniane.

> Jednak jeśli ta linia wykonuje się przed startem 
> procesu dziecka to jej wywołanie jest dla mnie logiczne.

Wykonuje się gdy rodzic zostaje poinformowany o zakończeniu potomka.
Inna sprawa że tu SIGCHLD powinien zostać zignorowany, skoro rodzic
i tak olewa tę informację (waitpid

> Zapobiegnie ew. 
> tworzeniu się kolejnych wątków.

Nie widzę o co ci chodzi z tworzeniem kolejnych wątków, ale to pewnie
przez to że wiem jak działają procesy w uniksach.

> Czy ktoś może wyjaśnić jak to jest w 
> rzeczywistości ?

google://linux+programmers+guide

-- 
Stanislaw Klekot
Re: wątki
#51694
Author: Paweł
Date: Thu, 03 Dec 2009 19:49
14 lines
1579 bytes
> 
> Nie widzę o co ci chodzi z tworzeniem kolejnych wątków, ale to pewnie
> przez to że wiem jak działają procesy w uniksach.
> 

Napisałem kiedyś podpierając się różnymi przykładami jeden (tylko jeden) 
skrypt w Perlu. Co pewien czas wymaga on dodania nowych funkcji ale 
spełnia swoje do dziś dość ważne zadanie. W ten sposób zostałem zostałem 
"specjalistą" w dziedzinie Perla. Proszę więc o wybaczenie jeśli używam 
niepoprawnych określeń. Moja rzeczywista wiedza na temat tego języka 
jest w zasadzie znikoma.

Paweł
Re: wątki
#51695
Author: Tomasz "rocku" K.
Date: Thu, 03 Dec 2009 21:02
49 lines
1905 bytes
Paweł wrote:
> Załóżmy, że program główny składa się z jednej pętli okresowo 
> wywoływanej. Chcę w tej pętli utworzyć nowy proces. Jednak tylko w 
> sytuacji gdy poprzedni się zakończył. Czy można zrobić to w ten sposób:
> 
> 
> my $t=0;
> while (1)
> {
> ...
> 
> if ($t==0)
>     {
>         defined (my $child=fork()) or die "Błąd wywołania fork():$!";
>     if ($child==0)
>         {
>         $t=1;
>         ...
> 
>         ...
>         $t=0;
>         exit 0;}
>     }
> 
> {

Nie można, ponieważ w momencie wywołania fork() tworzony jest drugi 
proces (dziecko), który posiada własną kopię zmiennej $t. Po jego 
wywołaniu zmienna $t w procesie rodzica wciąż będzie mieć wartość 0.

Jednym rozwiązaniem byłoby sprawdzenie w procesie rodzica czy dziecko 
jeszcze istnieje, np. tak:

$SIG{CHLD} = 'IGNORE';
my $child;
while(1) {
	unless (defined $child && kill 0, $child) {
		defined($child = fork()) or die $!;
		if (! $child) {
			print "I'm the child\n";
			exit 0;
		}
	}
	sleep 1;
}

-- 
Pozdrawiam,
Tomasz "rocku" K.
Re: wątki
#51696
Author: Paweł
Date: Thu, 03 Dec 2009 22:03
43 lines
2935 bytes
> Jednym rozwiązaniem byłoby sprawdzenie w procesie rodzica czy dziecko 
> jeszcze istnieje, np. tak:
> 
> $SIG{CHLD} = 'IGNORE';
> my $child;
> while(1) {
>     unless (defined $child && kill 0, $child) {
>         defined($child = fork()) or die $!;
>         if (! $child) {
>             print "I'm the child\n";
>             exit 0;
>         }
>     }
>     sleep 1;
> }
> 

W moim przypadku chyba nie można zrobić tego w ten sposób.
Skrypt jest uruchomiony jako demon i odbiera dane przez sieć z kilku 
źródeł. Dla każdego źródła wykonywane są identyczne operacje ale dla 
każdego niezależnie.
Proces rodzica zapisuje odebrane dane na lokalnym dysku we właściwych 
dla danego źródła plikach. To musi działać stale, bez żadnych przerw.

Dodatkowo muszę przesyłać skompletowane w procesie rodzica dane na inne 
serwery (różne dla różnych źródeł danych). Nie ma gwarancji, że te 
zdalne serwery będą działały wystarczająco szybko aby udało się w czasie 
rzeczywistym wszystko do nich wysłać. Jednak dane na te serwery wysyła 
się w małych porcjach. Każda taka porcja stanowi oddzielną całość. Nic 
wielkiego się nie stanie jeśli jakaś paczka nie zostanie przesłana.
W celu wysłania takiej paczki danych do danego serwera chcę utworzyć 
proces dziecka. Przy czym kolejną porcję danych będę pomijał jeśli 
poprzednia nie zdołała się przesłać. Czyli czym wolniejszy ten zdalny 
serwer to więcej danych będzie tracone.

W celu zrealizowanie opisanej funkcjonalności przed wywołaniem procesu 
dziecka muszę sprawdzić czy poprzedni proces się zakończył.
Kłopot polega jednak na tym, że mogę mieć uruchomionych kilka różnych 
procesów dziecka dla różnych źródeł danych (dla rożnych serwerów). Musze 
więc sprawdzić czy zakończył się dany proces dziecka. Jak to zrobić ?

Paweł
Re: wątki
#51697
Author: Marcin Gryszkalis
Date: Fri, 04 Dec 2009 06:54
21 lines
1595 bytes
Paweł wrote:

> Musze
> więc sprawdzić czy zakończył się dany proces dziecka. Jak to zrobić ?

Tworząc tablicę pid-ów dzieci i sprawdzając funkcją waitpid z WNOHANG.

Z drugiej strony może spróbuj poszukać w CPAN-ie - jest sporo modułów, które 
ukrywają te wszystkie wnętrzności i działają całkiem sensownie (jedyny 
problem, że jeśli coś nie będzie działać do końca poprawnie to nie 
rozumiejąc jak to wszystko pracuje nie będziesz mógł poprawić).

np. Parallel::ForkManager, subs::parallel, Parallel::ForkControl, 
Parallel::Jobs i pewnie jeszcze parę innych


pozdrawiam
-- 
Marcin Gryszkalis, PGP 0x9F183FA3
jabber jid:mg@fork.pl, gg:2532994
http://the.fork.pl
Re: wątki
#51698
Author: Tomasz "rocku" K.
Date: Fri, 04 Dec 2009 09:32
13 lines
1450 bytes
Paweł wrote:
> W celu zrealizowanie opisanej funkcjonalności przed wywołaniem procesu 
> dziecka muszę sprawdzić czy poprzedni proces się zakończył.
> Kłopot polega jednak na tym, że mogę mieć uruchomionych kilka różnych 
> procesów dziecka dla różnych źródeł danych (dla rożnych serwerów). Musze 
> więc sprawdzić czy zakończył się dany proces dziecka. Jak to zrobić ?

fork() zwraca pid procesu dziecka. Możesz w prosty sposób zmapować 
serwery docelowe na odpowiadające im procesy dziecka, np. w hashu.

-- 
Pozdrawiam,
Tomasz "rocku" K.
Re: wątki
#51699
Author: Paweł
Date: Fri, 04 Dec 2009 22:00
14 lines
1625 bytes
> fork() zwraca pid procesu dziecka. Możesz w prosty sposób zmapować 
> serwery docelowe na odpowiadające im procesy dziecka, np. w hashu.
> 

Nie wiem jednak jak poprawnie sprawdzić czy dany proces jeszcze się 
wykonuje.
Wykombinowałem sobie taki kod:
$SIG{'CHLD'}=sub {my $pid = wait; print $pid."\n" };
Wyświetla on numer procesu dziecka, który się zakończył
Czy ten mechanizm mogę wykorzystać do usuwania elementów z wyżej 
wymienionego hasha ?

Paweł
Re: wątki
#51692
Author: Andrzej Adam Filip
Date: Fri, 04 Dec 2009 22:37
37 lines
2556 bytes
Paweł <pawel.neo@neostrada.pl> pisze:
>> fork() zwraca pid procesu dziecka. Możesz w prosty sposób zmapować
>> serwery docelowe na odpowiadające im procesy dziecka, np. w hashu.
>
> Nie wiem jednak jak poprawnie sprawdzić czy dany proces jeszcze się
> wykonuje.
> Wykombinowałem sobie taki kod:
> $SIG{'CHLD'}=sub {my $pid = wait; print $pid."\n" };
> Wyświetla on numer procesu dziecka, który się zakończył
> Czy ten mechanizm mogę wykorzystać do usuwania elementów z wyżej
> wymienionego hasha ?

Zasadniczo tak o ile pamiętasz że:

a) możesz dostać jeden SIGCHLD na kilka zakończeń procesów-potomków
   wiec lepiej stosować to co sugerują w "man perlipc"

use POSIX ":sys_wait_h";
sub REAPER {
  my $child;
  while (($child = waitpid(-1,WNOHANG)) > 0) {
    $Kid_Status{$child} = $?;
  }
  $SIG{CHLD} = \&REAPER;  # still loathe SysV
}
$SIG{CHLD} = \&REAPER;

b) W "złośliwym" przypadku potomek może się zakończyć zanim rodzic go
   zdąży wpisać do hasha :-)

A tak z ciekawości: dlaczego używasz procesów a nie wątków (threads)?

-- 
Andrzej Adam Filip : anfi@onet.eu : Andrzej.Filip@gmail.com
Zawsze jestem gotów się uczyć, chociaż nie zawsze chcę, żeby mnie uczono.
  -- Churchill, Winston (1874-1965)
[ http://groups.google.pl/groups?selm=2s5u0s3krz-9C4@lonnie.brudna.chmurka.net ]
Re: wątki
#51701
Author: Paweł
Date: Sat, 05 Dec 2009 08:23
29 lines
2025 bytes
> a) możesz dostać jeden SIGCHLD na kilka zakończeń procesów-potomków
>    wiec lepiej stosować to co sugerują w "man perlipc"
> 
> use POSIX ":sys_wait_h";
> sub REAPER {
>   my $child;
>   while (($child = waitpid(-1,WNOHANG)) > 0) {
>     $Kid_Status{$child} = $?;
>   }
>   $SIG{CHLD} = \&REAPER;  # still loathe SysV
> }
> $SIG{CHLD} = \&REAPER;

Sprawdziłem, że podany przykład działa. Jednak mam problem z jego 
zrozumieniem.
Co robi:
$SIG{CHLD} = \&REAPER;


> A tak z ciekawości: dlaczego używasz procesów a nie wątków (threads)?
> 

Domyślnie obsługa wątków w Perlu na serwerze jest wyłączona. Zapewne 
można ją jakoś dodać. Boję się, że jak zacznę coś zmieniać na serwerze 
to wszystko przestanie działać.


Paweł
Re: wątki
#51702
Author: Adam Osuchowski
Date: Sat, 05 Dec 2009 08:38
15 lines
1666 bytes
Paweł wrote:
> Sprawdziłem, że podany przykład działa. Jednak mam problem z jego 
> zrozumieniem.
> Co robi:
> $SIG{CHLD} = \&REAPER;

Ustanawia funkcję REAPER jako obsługującą sygnał SIGCHLD (sygnał
wysyłany do procesu-rodzica, gdy proces-potomek zostanie zatrzymany lub
zakończony).

Ponieważ poruszasz się na styku programowania systemowego i programowania
w Perlu, poczytaj jakiś poradnik do programowania systemowego w uniksie,
a następnie manuale perlowe (w tym wypadku opis jest w perlvar).
Zdecydowanie pomoże Ci to w zrozumieniu co, jak i dlaczego się dzieje,
co z czego wynika i pozwoli objąć problem z szerszej perspektywy.
Re: wątki
#51703
Author: Paweł
Date: Sat, 05 Dec 2009 18:36
13 lines
1628 bytes
>>   while (($child = waitpid(-1,WNOHANG)) > 0) {
>>     $Kid_Status{$child} = $?;
>>   }

Zrobiłem wszystko co chciałem opierając się na podanym przykładzie.
Wygląda, że skrypt działa tak jak trzeba.

Mam jednak jeszcze jedno pytanie. Czy może się zdarzyć aby "$?" wyżej 
podanym kodzie zawierało wartość inną niż zero ?


Paweł
Re: wątki
#51700
Author: Maciej Zielenkieiwcz
Date: Sat, 05 Dec 2009 19:05
14 lines
1663 bytes
On 2009-12-05 18:36, Paweł <pawel.neo@neostrada.pl> wrote:
>
>>>   while (($child = waitpid(-1,WNOHANG)) > 0) {
>>>     $Kid_Status{$child} = $?;
>>>   }
>
> Zrobiłem wszystko co chciałem opierając się na podanym przykładzie.
> Wygląda, że skrypt działa tak jak trzeba.
>
> Mam jednak jeszcze jedno pytanie. Czy może się zdarzyć aby "$?" wyżej 
> podanym kodzie zawierało wartość inną niż zero ?

Tak, jeżeli proces który się zakończył zakończy się z błędem (zwróci coś
różnego od zera).
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads