Gestion d'un processus avec timeout

Gestion d'un processus avec timeout - Perl - Programmation

Marsh Posté le 30-05-2008 à 11:43:15    

Bonjour,

 

J'ai un problème en Perl que je n'arrive pas à résoudre.

 

J'ai besoin de lancer une commande (en fait mon script perl gère un lot et lance la commande en séquence sur chaque élément du lot), et de l'arrêter si elle prend trop de temps.

 

J'ai creusé plusieurs méthodes, donc la plus "propre" semble être l'utilisation de alarm et die, mais ça ne marche pas dans mon cas.

 

Voici le code de "test" de la méthode, modifiée pour mon usage, à partir du code qu'on peut trouver un peu partout sur le net :

 
Code :
  1. #!/usr/bin/perl -w
  2. $gcom = "<ma commande>";
  3. $time = 5;
  4. sub execCom {
  5. $com = $_[0];
  6. eval {
  7.  local $SIG{ALRM} = sub { die "$com est parti en boucle\n" };
  8.  alarm $time;
  9.  system($com);
  10.  alarm 0;
  11. };
  12. if ($@) {
  13.  die "#3xyz# : $@\n";
  14.  # délai dépassé : time out
  15. } else {
  16.  # délai non dépassé
  17.  print "#8xyz# : Délai non dépassé\n";
  18. }
  19. }
  20. system ("touch 1.t" );
  21. &execCom("$gcom" );
  22. system ("touch 2.t" );
  23. &execCom("$gcom" );
  24. system ("touch 3.t" );
  25. &execCom("$gcom" );
  26. system ("touch 4.t" );
  27. &execCom("$gcom" );
  28. system ("touch fin.t" );
  29. system ("ll -rt *.t" );
 

Au final, seule la première commande est exécutée (les lignes 28 et 29). Elle dépasse le timeout, mais n'est pas arrêtée... Les lignes suivantes (30 et +) ne sont jamais exécutées.

 

Quelqu'un pourrait m'expliquer ?

 

J'ai aussi regardé du côté de "ulimit" en bash/sh et "limit cputime" en csh/tcsh, mais je me trouve face à un comportement étrange, et surtout, des impossibilité de changer les valeurs... De plus comme c'est dépendant du shell, j'ai moins confiance.

 

Merci de m'aider :)


Message édité par Predicator le 30-05-2008 à 11:45:10
Reply

Marsh Posté le 30-05-2008 à 11:43:15   

Reply

Marsh Posté le 30-05-2008 à 14:16:11    

Bon, à défaut de réponse et vu que c'est assez pressé, j'ai utilisé une autre méthode. Pas propre il est vrai, mais fonctionnelle (je n'ai pas trouvé de faille, même je ne doute pas qu'il y en ait une).
 
Donc l'idée est qu'un script A gère le lot, et appelle un script perl B, via la commande system.
 
Voici le contenu du script B (avec commentaire de debuggage non retirés) :
 

Code :
  1. #!/usr/bin/perl -w
  2. $gcom = $ARGV[0];
  3. $time = $ARGV[1];
  4. $pid = fork();
  5. if (not defined $pid) {
  6. print "Plus de ressources...\n";
  7. exit(1);
  8. }
  9. if ($pid == 0) {
  10. # fils, on lance le programme
  11. exec($gcom);
  12. } else {
  13. #print "$pid\n";
  14. $cpt = 0;
  15. while($cpt < $time) {
  16.  # On teste l'existence du pid
  17.  open COM, ("ps -p $pid|grep $pid|" );
  18.  @lines = <COM>;
  19.  close COM;
  20.  print "@lines\n";
  21.  if ($lines[0] =~ m/$pid/) {
  22.   print "$pid is running\n";
  23.   if ($lines[0] =~ m/defunct/) {
  24.    print "Attente du fils que le père meurt\n";
  25.    exit(0);
  26.   } else {
  27.    print "$cpt : Attente d'une seconde\n";
  28.    sleep(1);
  29.   }
  30.  } else {
  31.   exit(0);
  32.  }
  33.  $cpt++;
  34. }
  35. open COM, ("ps -p $pid|grep $pid|" );
  36. @lines = <COM>;
  37. close COM;
  38. if ($lines[0] =~ m/$pid/) {
  39.  print "Sortie de boucle, $pid is running\n";
  40.  print "$pid must be killed... Cya in Hell my friend !\n";
  41.  kill 9, $pid;
  42. }
  43. }


 
Je vais utiliser ce script dès cet après-midi, mais je reste ouvert à toute évolution, donc ne vous privez pas :)

Reply

Marsh Posté le 30-05-2008 à 15:41:30    

L'autre jour j'ai eu le même problème, je sais pas pourquoi, je m'en sortais pas avec l'alarm, ça bloquait souvent pour une raison que je n'ai pas réussi à déterminer. donc au final j'ai fait une méthode ou je lance un autre processus pour tuer l'autre.
 

Code :
  1. sub _find_started_process
  2. {
  3.     my $spawner = shift;
  4.     my $child = `ps -elf | awk '\$5 == $spawner { print \$4 }`;
  5.     chomp $child;
  6.     print STDERR "Real subprocess is $child\n" if ($child);
  7.     return $child;
  8. }
  9. sub _start_killer
  10. {
  11.     my ($pid, $gracetime) = @_;
  12.     my $killer = fork;
  13.     return $killer if $killer;
  14.     POSIX::setsid;
  15.     chdir '/';
  16.     #fork && exit;
  17.     print STDERR "Killer $$ started for PID $pid with gracetime $gracetime\n";
  18.     sleep $gracetime;
  19.     if (kill(0, $pid)) {
  20.         do {
  21.             print STDERR "Killing $pid\n";
  22.         } while (kill(9, $pid) != 1);
  23.     }
  24.     exit;
  25. }
  26. sub _run
  27. {
  28.     my $cmd = shift;
  29.     my $data = undef;
  30.     my $pid = undef;
  31.     my $killerpid = undef;
  32.     eval {
  33.         my $timeout = 20;
  34.         ($pid = open(INPUT, '-|', $cmd)) or die("cannot start : $!" );
  35.         print STDERR "Running $cmd with PID $pid\n";
  36.         my $started = _find_started_process($pid);
  37.         if ($started) {
  38.             $killerpid = _start_killer($started, $timeout);
  39.         }
  40.         $data = do { local($/); <INPUT> };
  41.         close(INPUT);
  42.     };
  43.     my $error = @_;
  44.     if ($killerpid && kill(0, $killerpid)) {
  45.         do {
  46.             print STDERR "Killing killer $killerpid\n";
  47.         } while (kill(9, $killerpid) != 1);
  48.         waitpid($killerpid, 0);
  49.     }
  50.     # _start_killer($killerpid, 0);
  51.     if ($error) {
  52.         die "Couldn't not run '$cmd' : $error";
  53.     }
  54.     return $data;
  55. }

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed