Schrift
[thread]8144[/thread]

Perl - Mehrere Child-Prozesse starten

Leser: 2


<< >> 10 Einträge, 1 Seite
Digioso
 2006-07-06 14:43
#67887 #67887
User since
2006-07-06
8 Artikel
BenutzerIn
[default_avatar]
Hallo.

Also, ich will ein Perl-Programm schreiben, welches Befehle aus einer Text-Datei einliesst und diese dann ausführt.
Diese Befehle werden mittels pipe an Kind-Prozesse (fork) weitergegeben.
Es dürfen (und sollen) allerdings nur vier Kind-Prozesse gleichzeitig laufen.
Da mehr als vier Kommandos ausgeführt werden sollen, muss immer gewartet werden, wenn bereits vier Prozesse existieren.

Funktioniert auch alles soweit, nur habe ich das Problem, dass zwar vier Prozesse gleichzeitig laufen, aber nur drei davon ein Kommando gesendet bekommen, sprich nur drei Prozesse richtig arbeiten.

In der sleep.txt Datei stehen die Kommandos. Diese belaufen sich momentan auf den sleep Befehl mit verschiedenen Angaben. (sleep 10, sleep 15, ...)

Das ganze soll unter Unix (Solaris 8 ) laufen.

Mein Code bisher:

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#!/bin/perl -w
use strict;

pipe(READER,WRITER); # used for communication between child and parent

#$SIG{'CHLD'} = 'reaper'; # used for killing zombies

my $zahl = 0; # number of currently running children
my $zeile; # row
my $pid; # process ID

if (open FILE, "< /sls_backup/mdjanan/sleep.txt")
{
while($zeile = <FILE>)
{
if ($zahl < 4)
{
$zahl++; # increase number of children
$pid = fork(); # a new child is born. yay :)
print $pid."\n";
}
if($pid == 0)
{
# child-process
#close WRITER;
# wait for command from parent
while (my $line = <READER>)
{
print "$line\n";
exec($line); # execute command
}
}
else
{
#close READER;
# send child a command
sleep 1;
print WRITER $zeile;
if($zahl == 4) # four children are running. wait till one of them finishes.
{
while(! wait()) # *waiting*
{
}
print "returncode ".$?."\n";
$zahl--; # decrease number of children
}
}
}
}
else
{
print "\nCan't open file /sls_backup/mdjanan/sleep.txt\n";
}
exit 0;

# Waits for zombies
#sub reaper
#{
# wait(); # grab a zombie
#} # sub reaper


Hier das ganze in ps:

Code: (dl )
1
2
3
4
5
6
7
ps -ef | grep sleep
root 25610 13616 0 11:17:02 pts/1 0:00 /bin/perl -w ./sleep.pl
root 25625 25610 0 11:17:07 pts/1 0:00 sleep 12
root 25614 25610 0 11:17:02 pts/1 0:00 sleep 11
root 25613 25610 0 11:17:02 pts/1 0:00 sleep 15
root 25634 25610 0 11:17:12 pts/1 0:00 /bin/perl -w ./sleep.pl
root 25636 17134 0 11:17:13 pts/2 0:00 grep sleep
\n\n

<!--EDIT|Digioso|1152182751-->
betterworld
 2006-07-06 16:13
#67888 #67888
User since
2003-08-21
2613 Artikel
ModeratorIn

user image
Ich denke mal, dass es sich hier auch wieder um ein Pufferungsproblem handelt. Du schickst etwas auf eine Pipe, von der alle Kinder lesen koennen. Und da der <>-Operator gepuffert ist, liest ein Kind nicht nur eine Zeile, sondern alles was da ist. Das koennte unter Umstaenden dann auch noch die Zeile fuer das naechste Kind beinhalten.
betterworld
 2006-07-06 16:15
#67889 #67889
User since
2003-08-21
2613 Artikel
ModeratorIn

user image
Als Loesung wuerde ungepuffertes (zeichenweises) Lesen aber wohl auch nicht helfen, weil es dabei Race-Conditions geben wird. Ich wuerde also eine der beiden folgenden Loesungen waehlen:

1) Mit socketpair ein Datagram-Socket machen, weil Datagrams atomar gelesen werden koennen
2) Fuer jedes Kind eine eigene Pipe bauen\n\n

<!--EDIT|betterworld|1152188345-->
Digioso
 2006-07-06 16:21
#67890 #67890
User since
2006-07-06
8 Artikel
BenutzerIn
[default_avatar]
Jedes Kind bekommt nur eine Nachricht durch die Pipe.
Danach überschreibt sich das Kind ja selber mittels exec und anschließend wird das nächste Kind erzeugt.
Sprich es gibt jedes Mal nur ein Kind, welches auf die Pipe wartet.

Ich konnte es jetzt "lösen", indem ich fünf Kinder erstellen lasse.
Davon werden bekommen vier ihre Nachricht und das fünfte schaut wieder nur zu.

Is allerdings trotzdem komisch. :/\n\n

<!--EDIT|Digioso|1152188619-->
betterworld
 2006-07-06 16:26
#67891 #67891
User since
2003-08-21
2613 Artikel
ModeratorIn

user image
[quote=Digioso,06.07.2006, 14:21]Jedes Kind bekommt nur eine Nachricht durch die Pipe.[/quote]
Ja, aber es liest mehr als eine Zeile. Das Problem hatten wir ja auch in diesem Thread: http://board.perl-community.de/cgi-bin....;t=156;
Digioso
 2006-07-06 16:38
#67892 #67892
User since
2006-07-06
8 Artikel
BenutzerIn
[default_avatar]
Da jedes Kind nur eine Zeile zugeschickt bekommt, ist das doch eigentlich egal, oder?

Es werden jedenfalls alle Zeilen der Datei verarbeitet.

Achja, die Pipe sollte nun auch sofort geflusht werden.

Code: (dl )
1
2
print WRITER $zeile; # send child a command
$| = 1; # flush pipe
\n\n

<!--EDIT|Digioso|1152189557-->
betterworld
 2006-07-06 16:42
#67893 #67893
User since
2003-08-21
2613 Artikel
ModeratorIn

user image
Warum meinst Du denn, dass jedes Kind nur eine Zeile zugeschickt bekommt? Alle Kinder teilen sich dieselbe Pipe, alle lesen von demselben Dateideskriptor. Und jeweils dasjenige Kind, das zuerst zum Lesen kommt, liest alles, was da ist. Wer zuerst kommt, mahlt zuerst.

Du schreibst immer nur auf WRITER und sagst niemandem, an wen Du schreibst.\n\n

<!--EDIT|betterworld|1152189876-->
betterworld
 2006-07-06 16:50
#67894 #67894
User since
2003-08-21
2613 Artikel
ModeratorIn

user image
Außerdem fällt mir an Deinem Script gerade auf, dass Du, wenn $zahl == 4 ist, zwar nicht forkst aber trotzdem einen Befehl auf die Pipe schreibst.\n\n

<!--EDIT|betterworld|1152190237-->
Digioso
 2006-07-07 11:16
#67895 #67895
User since
2006-07-06
8 Artikel
BenutzerIn
[default_avatar]
Es gibt zu jedem Zeitpunkt nur ein Kind, welches auf den READER hört. Folglich kann auch nur ein Kind was durch die Pipe empfangen.
Durch exec wird das Kind komplett überschrieben und führt nur noch das Kommando aus. Es hört also nicht mehr den READER ab.

Code: (dl )
1
2
3
4
5
6
if ($zahl < 4)
{
$zahl++; # increase number of children
$pid = fork(); # a new child is born. yay :)
print $pid."\n";
}


Sobald es drei aktive Kinder gibt, wird das vierte erzeugt und danach steht $zahl auf 4.
Dem vierten Kind wird dann mittels pipe der Befehl zugesendet.
Da $zahl == 4, landet er jetzt in der Schleife.

Code: (dl )
1
2
3
while(! wait()) # *waiting*
{
}


Dort wird gewartet, bis sich irgendeines der Kinder beendet hat.
Anschließend wird $zahl wieder um 1 verringert, da ja nur noch drei Kinder laufen.
Er holt sich das nächste Kommando aus der Textdatei und erstellt wieder ein neues Child.
Das Child hört auf den READER und das Hauptprgramm schickt was durch den WRITER.
Das Child ersetzt sich wieder selbst.
usw...

Woran ich gedacht habe, war, dass das Hauptprogramm vielleicht zu schnell sendet und ein neues Kind erstellt, bevor das andere sich seinen Befehl abgeholt hat.
Das wäre dann ja genau das, was du angesprochen hast.
Aus dem Grund habe ich im Hauptprogramm mal ein sleep 1 eingebaut, allerdings hat das auch nichts gebracht.\n\n

<!--EDIT|Digioso|1152256948-->
Digioso
 2006-07-07 11:25
#67896 #67896
User since
2006-07-06
8 Artikel
BenutzerIn
[default_avatar]
Atm. sieht das Programm jetzt so aus und startet auch vier Children, die ihre Befehle ausführen. Nur gibt es nach wie vor ein inaktives Child:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#!/bin/perl -w
use strict;

pipe(READER,WRITER); # used for communication between child and parent

my $zahl = 0; # number of currently running children
my $zeile; # row
my $pid; # process ID

if (open FILE, "< /sls_backup/mdjanan/sleep.txt")
{
while($zeile = <FILE>)
{
if ($zahl < 5)
{
$zahl++; # increase number of children
$pid = fork(); # a new child is born. yay :)
} # if ($zahl < 5)
if($pid != 0)
{
# parent-process
print WRITER $zeile; # send child a command
$| = 1; # flush pipe
if($zahl == 5) # four children are running. wait till one of them finishes.
{
while(! wait()) # *waiting*
{
} # while(! wait())
$zahl--; # decrease number of children
} # if($zahl == 5)
} # if($pid != 0)
else
{
# child-process
# wait for command from parent
while (my $line = <READER>)
{
exec($line); # execute command
} # while (my $line = <READER>)
} # else
} # while($zeile = <FILE>)
} # if (open FILE, "< /sls_backup/mdjanan/sleep.txt")
else
{
print "\nCan't open file /sls_backup/mdjanan/sleep.txt\n";
} # else
exit 0;
<< >> 10 Einträge, 1 Seite



View all threads created 2006-07-06 14:43.