jeudi 15 octobre 2009

... de lignes de perl pour illustrer un fork

Ceci est un vieux message je l'avais commencé en juin 2008 et jamais publié. Le principe était de rédiger un exemple d'utilisation de fork() en perl. Je n'ai pas réellement relu le code et il nécessiterait un bon coup de balai, il aura au moins le mérite d'être publié. Le résultat de l'exécution est à la suite.




#!/usr/bin/perl

#
# Ce programme est un exemple d'utilisation de fork() en perl
#


# On sort le texte à l'écran dès qu'il est pret

$|
= 1;

use strict;
use warnings;

# Attente de fork() sans attente (WNOHANG)
use POSIX ":sys_wait_h" ;


# Tableau de données à traiter
my @array = qw(rt_01 rt_02 lb_01 lb_02 lb_03 sw_01 sw_02 sw_03 rt_03 rt_04 rt_05 rt_06 rt_07 fw_01 fw_02);

# Nombre de traitements simultanés maximum:
my $taches_max = 4;

# Un tableau de hashage permettant de controller l'affichage de messages ...
my %debug = (
'child_fork' => 0, # ... à la création d'un fils par le père
'child_inner' => 0, # ... pendant le traitement d'un fils
'child_exit' => 0, # ... lorsque le fils va traiter
'queue' => 1, # ... quand on change l'etat de la queue
) ;

########################################################################

# Variables internes
my %taches ; # Hash contenant les PID des fils
my $taches_size = 0 ; # Nombre d'éléments dans le hash
my $left = @array ; # Elements restant à traiter

sub show_taches() {
return unless $debug{'queue'};
printf "Etat de la queue : [%s/%s]", $taches_size, $taches_max ;
if( ($left - $taches_size) ) {
printf ", %s tâches en attente", ($left - $taches_size);
}
print "\n";
}

print "Début du traitement de $left tâches\n";
show_taches();


# Boucle sur tout les équipements
foreach my $equipement (@array) {
my $pid = fork ;

# Si on a un pid c'est celui du fiston qui a été spawné
if ($pid) {
print "$$> child forké avec le pid $pid.\n" if $debug{'child_fork'};
# On enregistre le pid du fils
$taches{$pid} = 1;
$taches_size++;
show_taches();

# Si le pid renvoyé est 0 on est dans le process forké
} elsif ($pid == 0) {
# fils
my $sleep_for = 1 + int rand(5) ;
print "$$> $equipement fait une sieste de $sleep_for seconds.\n" if $debug{'child_inner'};
sleep $sleep_for ;
print "$$> Réveil de $equipement après $sleep_for s.).\n" if $debug{'child_exit'};
exit(0);

# Sinon (valeur négative) il y a eu une erreur
} else { die "Erreur de fork : $!\n"; }

# On attend jusqu'à ce qu'une tâche se finisse ou que l'on puisse encore en lancer
my $child_pid ;
do {
# waitpid -1 attend un changement de statut de n'importe quel fils
# renvoi le pid d'un child qui a bougé ou 0 si rien n'a changé
$child_pid = waitpid( -1, WNOHANG );

# Le child est sorti, on le retire des tâches en cours
if( $child_pid > 0 ) {
delete $taches{$child_pid};
$taches_size-- ;
$left--;
show_taches();
}
} until(
( ($child_pid > 0) or ($taches_size < $taches_max) ) and ( ($left == 0) or ( $taches_size < $left ) ) ); } print "Fin du traitement\n";


La sortie du programme est ci-dessous. Malheureusement il est difficile de représenter les temps d'attente. C'est peut-être une idée d'amélioration.


Début du traitement de 15 tâches
Etat de la queue : [0/4], 15 tâches en attente
Etat de la queue : [1/4], 14 tâches en attente
Etat de la queue : [2/4], 13 tâches en attente
Etat de la queue : [3/4], 12 tâches en attente
Etat de la queue : [4/4], 11 tâches en attente
Etat de la queue : [3/4], 11 tâches en attente
Etat de la queue : [4/4], 10 tâches en attente
Etat de la queue : [3/4], 10 tâches en attente
Etat de la queue : [4/4], 9 tâches en attente
Etat de la queue : [3/4], 9 tâches en attente
Etat de la queue : [4/4], 8 tâches en attente
Etat de la queue : [3/4], 8 tâches en attente
Etat de la queue : [4/4], 7 tâches en attente
Etat de la queue : [3/4], 7 tâches en attente
Etat de la queue : [4/4], 6 tâches en attente
Etat de la queue : [3/4], 6 tâches en attente
Etat de la queue : [4/4], 5 tâches en attente
Etat de la queue : [3/4], 5 tâches en attente
Etat de la queue : [4/4], 4 tâches en attente
Etat de la queue : [3/4], 4 tâches en attente
Etat de la queue : [4/4], 3 tâches en attente
Etat de la queue : [3/4], 3 tâches en attente
Etat de la queue : [4/4], 2 tâches en attente
Etat de la queue : [3/4], 2 tâches en attente
Etat de la queue : [4/4], 1 tâches en attente
Etat de la queue : [3/4], 1 tâches en attente
Etat de la queue : [4/4]
Etat de la queue : [3/4]
Etat de la queue : [2/4]
Etat de la queue : [1/4]
Etat de la queue : [0/4]
Fin du traitement

Aucun commentaire: