Fork et Sortie Standard

Le
Sébastien Cottalorda
Bonjour à tous,

Sous Linux, j'ai developpé un programme qui en gère plusieurs autres en
se forkant.

Voilà le principe:
Dans une table de hachage, se trouvent toutes les commandes à lancer.
Pour chaque commande à lancer:
il se forke, récupère le PID du fil, puis passe au suivant, etc
Le fils va alors lancer la commande grâce à exec, de manière à ne pas
ouvrir de shell supplémentaires, et permettre au père de conserver le
PID comme étant celui de la commande à gérer.

Chaque commande est lancée, gràce à exec, avec une redirection de STDOUT
et STDERR vers un fichier de log.

Et c'est là ou le bas blesse.

En réalité, je récupère toutes les sorties standard sur le programme
père, se qui génère, vous vous en doutez bien, un flux d'informations
colossal.

Je n'ai pas la maitrise des programmes lancés, je ne peux pas les faire
"imprimer" dans un log autrement qu'en redirigeant la sortie standard.

Y aurait-il un moyen ?

Voici mon script:
#
#!/usr/bin/perl -w
use strict;
use warnings;
use Config;
use POSIX ":sys_wait_h";

$0="MASTER";
defined $Config{sig_name} || die "No sigs?";
my $i=0;
my @signame=();

foreach (split(' ', $Config{sig_name})) {
$signame[$i] = $_;
$i++;
}
my %cmd=(
"PROC0" => "perl programme.pl fichier1 > fichier1.log 2>&1",
"PROC1" => "perl programme.pl fichier2 > fichier2.log 2>&1",
"PROC2" => "perl programme.pl fichier3 > fichier3.log 2>&1",
"PROC3" => "perl programme.pl fichier4 > fichier4.log 2>&1"
);
my @pid=();
my $cloture=0;
my %Kid=();
my %Dik=();
my %tosend = %cmd;

while(1) {
foreach my $process (sort keys %tosend){
my $pid = fork();
unless (defined($pid)){
&logmsg(__LINE__,"Fork impossible - $! ");
exit 1;
}
if ($pid == 0){
delete $tosend{$process};
$0="PROC_$process";
print "FILS --> $process : Lancement commande
$cmd{$process} .";
exec split/s+/, $cmd{$process} ;
exit;
}
else {
delete $tosend{$process};
$0="MASTER";
print "PERE: Nouveau Fils: PID N°$pid";
$Dik{$pid} = $process;
$Kid{$process}=$pid;
}
sleep 3;
}
sleep 3;
last unless (keys %Kid); # s'il n'y a plus de fils à gérer, Bye
}
print "Terminé";
exit;


sub REAPER {
my $child;
my $numsignal;
while (($child = waitpid(-1,WNOHANG)) > 0) {
print "REAPER: child=$child [$Dik{$child}] renvoi $?";
$numsignal = $? & 127;
print "REAPER: child=$child [$Dik{$child}] recv signal
signame[$numsignal]($numsignal)";
unless ($cloture){
if ($numsignal > 1){ # Voir les n° en gestion ==> à une cloture
print "Programme relancement de $Dik{$child} ";
$tosend{$Dik{$child}} = $cmd{$Dik{$child}};
}
}
}
$SIG{CHLD} = &REAPER; # still loathe sysV
}

$SIG{CHLD} = &REAPER;

$SIG{INT} = sub { &KILL_ALL("INT")};
$SIG{HUP} = sub { &KILL_ALL("HUP")};
$SIG{ABRT} = sub { &KILL_ALL("ABRT")};
$SIG{TERM} = sub { &KILL_ALL("TERM")};
$SIG{QUIT} = sub { &KILL_ALL("QUIT")};

sub KILL_ALL {
print "PERE RECU ".$_[0]."";
print "Killing all son";
$cloture=1; # permet d'empêcher le relancement
foreach (sort {$b cmp $a} keys %Kid){
print "Sending TERM(15) to $_ => pid n° $Kid{$_}";
kill 15, $Kid{$_};
}
exit 0;
}

#
Cela me permet d'avoir ceci (gràce à la commande ps axf):

MASTER
_ perl programme.pl fichier1 > fichier1.log 2>&1
_ perl programme.pl fichier2 > fichier2.log 2>&1
_ perl programme.pl fichier3 > fichier3.log 2>&1
_ perl programme.pl fichier4 > fichier4.log 2>&1


Et voici la sortie standard du MASTER, et de lui seulement.

FILS --> PROC0 : Lancement commande perl programme.pl fichier1 >
fichier1.log 2>&1 .
PERE: Nouveau Fils: PID N°1667
20060831154810 sortie du fichier1
FILS --> PROC1 : Lancement commande perl programme.pl fichier2 >
fichier2.log 2>&1 .
PERE: Nouveau Fils: PID N°1668
20060831154813 sortie du fichier2
FILS --> PROC2 : Lancement commande perl programme.pl fichier3 >
fichier3.log 2>&1 .
PERE: Nouveau Fils: PID N°1669
20060831154816 sortie du fichier3
FILS --> PROC3 : Lancement commande perl programme.pl fichier4 >
fichier4.log 2>&1 .
PERE: Nouveau Fils: PID N°1671
20060831154819 sortie du fichier4
20060831154820 sortie du fichier1
20060831154823 sortie du fichier2
20060831154826 sortie du fichier3
20060831154829 sortie du fichier4
20060831154830 sortie du fichier1

etc, etc

par contre, absolument rien dans fichier1.log, fichier 4.log

Merci de toute aide.

Sébastien
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Nicolas George
Le #126513
Sébastien Cottalorda wrote in message
"PROC0" => "perl programme.pl fichier1 > fichier1.log 2>&1",

exec split/s+/, $cmd{$process} ;


Tu ne peux pas faire une redirection avec exec : là, tu es en train
d'appeler le script programme.pl (soit dit en passant, l'extension
recommandée est .plx) avec comme arguments fichier1, >, fichier1.log et
2>&1. Les redirections à base de > sont interprétées par un shell ; si tu
veux te passer de shell, ce qui est une bonne idée, il faut les faire
toi-même, avec un open sur STDOUT.

Sébastien Cottalorda
Le #126512
Sébastien Cottalorda wrote in message

"PROC0" => "perl programme.pl fichier1 > fichier1.log 2>&1",



exec split/s+/, $cmd{$process} ;



Tu ne peux pas faire une redirection avec exec : là, tu es en train
d'appeler le script programme.pl (soit dit en passant, l'extension
recommandée est .plx) avec comme arguments fichier1, >, fichier1.log et
2>&1.
ça marche quand même comme cela, mais ce n'est pas là mon problème.


Les redirections à base de > sont interprétées par un shell ; si tu
veux te passer de shell, ce qui est une bonne idée, il faut les faire
toi-même, avec un open sur STDOUT.


dans mon exemple, cela se traduira par quoi ?
Je n'ai pas très bien compris, ai-je un moyen "externe", en gros, à
partir du programme que j'ai envoyé, d'intercepter, chaqua flux STDOUT ?

j'ai eu beau remplacer l'exec par un:

unless (open (FH, "$cmd |")){
die "Problemen";
}
while ($my $ligne=<FH>){
print "Intercepté>> $lignen";
}
close(FH);

j'ai même essayé avec un open2, mais je n'ai rien de rien.....

mais cela ne marche pas non plus.
En fait cela crée un fils au fils, ce qui n'est pas ce que je recherche.


Nicolas George
Le #126510
Sébastien Cottalorda wrote in message
ça marche quand même comme cela, mais ce n'est pas là mon problème.


Probablement que les programmes lancés ignorent leurs arguments
supplémentaires.

dans mon exemple, cela se traduira par quoi ?


Il faut reprendre un peu la structure du programme, parce que les structures
de données ne sont pas bien adaptées.

Je n'ai pas très bien compris, ai-je un moyen "externe", en gros, à
partir du programme que j'ai envoyé, d'intercepter, chaqua flux STDOUT ?


Il ne faut pas faire :

exec "foo", "bar", ">", "baz", "2>&1";

(c'est ce qu'il y a dans ton programme, déguisé par un split) mais :

open STDOUT, ">", "bar";
open STDERR, ">&STDOUT";
exec "foo", "bar";

Avec bien sûr tous les tests d'erreur qui s'imposent.

Sébastien Cottalorda
Le #126509
[snip]

Je n'ai pas très bien compris, ai-je un moyen "externe", en gros, à
partir du programme que j'ai envoyé, d'intercepter, chaqua flux STDOUT ?



Il ne faut pas faire :

exec "foo", "bar", ">", "baz", "2>&1";

(c'est ce qu'il y a dans ton programme, déguisé par un split) mais :

open STDOUT, ">", "bar";
open STDERR, ">&STDOUT";
exec "foo", "bar";

Avec bien sûr tous les tests d'erreur qui s'imposent.
J'ai adapté, mais cela ne marche toujours pas.


Les fichiers log sont bien crées, mais restent désespéréments vides.
Même après un long temps d'attente ... (bufferisation ?)

#============================================================================ my %cmd=(
"PROC0" => {
'prog' => "/home/workspace/I-Cars/programme.pl fichier1",
'log' => "fichier1.log",
},
"PROC1" => {
'prog' => "/home/workspace/I-Cars/programme.pl fichier2",
'log' => "fichier2.log",
},
"PROC2" => {
'prog' => "/home/workspace/I-Cars/programme.pl fichier3",
'log' => "fichier3.log",
},
"PROC3" => {
'prog' => "/home/workspace/I-Cars/programme.pl fichier4",
'log' => "fichier4.log",
},
);
[snip]
while(1) {
foreach my $process (sort keys %tosend){
my $pid = fork();
unless (defined($pid)){
&logmsg(__LINE__,"Fork impossible - $! n");
exit 1;
}
if ($pid == 0){
delete $tosend{$process};
$0="PROC_$process";
print "FILS --> $process : Lancement commande
$cmd{$process}{prog} ....n";
unless (open (STDOUT, ">", $cmd{$process}{log}) ){
die "Can't open $cmd{$process}{log} $!n";
}
unless (open (STDERR, ">&STDOUT")){
die "Can't redirect STDERR to STDOUT $!n";
}
exec "/usr/bin/perl", (split /s+/, $cmd{$process}{prog});
exit;
}
else {
delete $tosend{$process};
$0="MASTER";
print "PERE: Nouveau Fils: PID N°$pidn";
$Dik{$pid} = $process;
$Kid{$process}=$pid;
}
sleep 3;
}
sleep 3;
last unless (keys %Kid); # s'il n'y a plus de fils à gérer, Bye
}
print "Terminén";
exit;


Nicolas George
Le #126381
Sébastien Cottalorda wrote in message
J'ai adapté, mais cela ne marche toujours pas.

Les fichiers log sont bien crées, mais restent désespéréments vides.
Même après un long temps d'attente ... (bufferisation ?)


Chez moi ça marche (tm), avec /bin/echo comme programme à lancer.

Sébastien Cottalorda
Le #126380
Sébastien Cottalorda wrote in message

J'ai adapté, mais cela ne marche toujours pas.

Les fichiers log sont bien crées, mais restent désespéréments vides.
Même après un long temps d'attente ... (bufferisation ?)



Chez moi ça marche (tm), avec /bin/echo comme programme à lancer.
Tu as raison, finalement, ça marche chez moi aussi, mais après un très

long temps => il doit y avoir une bufferisation quelque part :

pourtant, j'ai bien mis $|=1 en début de script ....

je continu de chercher.

Merci pour l'aide que tu m'as apporté.


Sébastien Cottalorda
Le #126379
Sébastien Cottalorda wrote in message

J'ai adapté, mais cela ne marche toujours pas.

Les fichiers log sont bien crées, mais restent désespéréments vides.
Même après un long temps d'attente ... (bufferisation ?)



Chez moi ça marche (tm), avec /bin/echo comme programme à lancer.
ça y est, ça marche parfaitement aussi.


C'était le programme "emetteur de STDOUT" qui n'avait pas son $|=1

Merci encore pour ta précieuse aide.


Publicité
Poster une réponse
Anonyme