Página 1 de 1

Alternativa a system()

NotaPublicado: 2009-07-24 06:33 @315
por kondenado
Buenas,
Luego de buscar en Google, perldoc y aquí mismo, aún no he podido resolver todas mis dudas, así que espero me puedan ayudar :)

Sucede que estoy trabajando en un proyecto el cual actualmente "corre" bajo Linux. Mi intención es que sea multiplataforma, aunque aún existen un par de líneas de código que me impiden aquello.

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
system("cd $dir_temporal;./$ejecutable < $fichero_entrada > $fichero_salida");
 
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4


Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
system("ps -eo pid | grep $ejecutable");
`kill -9 $ppid` if (-e '/proc/'.$ppid.'/status');
 
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Para aclarar un poco, el directorio donde se ejecutan los binarios no es el mismo donde está el programa. Además, el ejecutable lee los datos de un fichero de entrada y genera un fichero de salida. Cabe señalar que los ejecutables son enviados por los usuarios, por eso utilizo kill para asegurarme que no hayan quedado como zombies.

Con respecto a los comandos anteriores, he encontrado alternativa a cd (chdir) y kill (kill). Aun así con éste último no logro solucionar el tema de saber el pid del programa ejecutado.

Siguiendo con los comandos:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
my $diff = `diff $salida_original $salida_generada`;
if($diff) ....
 
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


He buscado en CPAN algún módulo que cumpla exactamente la misma función que el comando diff de Linux pero no lo he encontrado, o quizás he buscado mal. Nunca está demás crear una subrutina que abra los dos ficheros y los compare línea a línea, pero preferiría saber si ya existe algún módulo.

Por último, agradecería si alguien pudiera explicarme un poco acerca de las señales en Perl, más específicamente de aquellas que son enviadas para matar al proceso. Por ejemplo cuando un programa termina su ejecución abruptamente (digamos Ctrl+C) la ejecución del bloque de código END {} no se realiza. ¿Se puede realizar esto manejando las señales?

De antemano gracias.

¡Saludos!

Re: Alternativa a system()

NotaPublicado: 2009-07-24 07:38 @360
por explorer
Prueba con esto: http://search.cpan.org/search?query=diff&mode=module

No has dicho a qué otros sistemas quieres portar el programa, pero, si es Windows, podrías usar sus módulos para interaccionar con el sistema.

Usando la variable especial $^O puedes hacer comprobaciones (if) para hacer una cosa u otra según el sistema en que estés.

En Windows, para saber el pid de los procesos puedes usar Win32::Process::List para saber la lista de procesos, y con Win32::Process::Kill, acabar con ellos.

Re: Alternativa a system()

NotaPublicado: 2009-07-24 17:48 @783
por kondenado
Ya había buscado en CPAN y no me habían convencido los módulos que había encontrado. Más que nada por el tema de las dependencias. Pero estaba equivocado, acabo de bajar Algorithm::Diff y me ha funcionado a la primera, sin instalar nada y de maravilla :)

Con respecto a los S.O. a los cuales quisiera portar el programa, serían principalmente Windows y *BSD. Los comando que se utilizan son tan básicos que para portar a *BSD no hay que cambiar nada. En cuanto a Windows, gracias explorer por la idea, no había pensado en utilizar dicha variable.

Una pregunta, ¿los módulos que mencionaste vienen pre-instalados en el intérprete para Windows? Actualmente no tengo acceso a ningún ordenador con Windows.

Por último, ¿alguna idea de cómo intervenir cuando un programa es cerrado abruptamente?

Gracias.

Re: Alternativa a system()

NotaPublicado: 2009-07-24 18:12 @800
por explorer
Los módulos que vienen en las distribuciones incluidos de base, se dice que pertenecen al CORE, así que si no están en ese listado, habrá que instalarles aparte.

En cuanto a la captura de señales, veo en la documentación de Perl la presencia del pragma sigtrap, que puedes usar para definir la función que quieres que se ejecute cuando ocurra una señal o tipo de señales.

Mira también la variable especial %SIG, con la que podrás hacer lo mismo.

Re: Alternativa a system()

NotaPublicado: 2009-07-25 00:39 @069
por kondenado
Gracias por la respuesta.

Efectivamente con %SIG pude hacer control de varias señales, como por ejemplo, las que recibe el programa cuando termina abruptamente. Aunque no todas pueden ser manejadas. Cuando matas un proceso con kill -9, éste morirá si o sí y no se puede evitar. Lo mismo pasa con die.

Dejaré algunos ejemplos por si alguien los llegara a necesitar algún día.

Evitar que programa termine al presionar Ctrl+C
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. $SIG{'INT'} = 'IGNORE';
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4



Manejar término de programa (Ctrl+C) con una subrutina propia
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. sub handler{
  2.        print "I wanna live!\n";
  3.        live;
  4. }
  5.  
  6. sub live{
  7.       while(1){
  8.              print "You're alive!\n";
  9.              sleep 10;
  10.       }
  11. }
  12.  
  13. $SIG{'INT'} = \&handler;
  14.  
  15. live;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Manejo de die
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. sub die_handler{
  2.          print "¡Adios!\n";
  3.          EscribeErrorLog($!); # Mejor manejo de errores
  4. }
  5. $SIG{__DIE__} = \&die_handler;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Re: Alternativa a system()

NotaPublicado: 2009-07-25 20:45 @906
por kondenado
Buenas,

Ya he encontrado la manera de manejar los procesos de Linux a través de un módulo y no de llamadas de sistema. Además, encontré una manera más efectiva de eliminar los zombis. Espero le sea de ayuda a alguien:

Antes que todo, hay que instalar el módulo Proc::ProcessTable. (Orientado a quienes no poseen permisos de superusuario).

NOTA: $USER debe ser definido por cada usuario
Sintáxis: [ Descargar ] [ Ocultar ]
Using bash Syntax Highlighting
  1. cd /home/$USER
  2. mkdir lib
  3. mkdir bin
  4. wget http://search.cpan.org/CPAN/authors/id/D/DU/DURIST/Proc-ProcessTable-0.45.tar.gz
  5. gunzip Proc-ProcessTable-0.45.tar.gz
  6. tar -xvf Proc-ProcessTable-0.45.tar
  7. cd Proc-ProcessTable-0.45
  8. perl Makefile.PL PREFIX=/home/$USER
  9. make
  10. make test
  11. make install
  12. make clean
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4


Luego:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. use lib "/home/$USER/lib/perl/5.10.0";
  2. use strict;
  3. use Proc::ProcessTable;
  4.  
  5. my $table = Proc::ProcessTable->new;
  6. foreach my $proc( @{$table->table} ){
  7.      my ($pid, $cmnd) = ($proc->pid, $proc->cmndline);
  8.      print "PID: $pid\nCMND: $cmnd\n";
  9.      print '-' x 60;
  10.      print "\n";    
  11. }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Más opciones en Proc::ProcessTable::Process.

Para eliminar procesos zombis haremos uso de Proc::Killall.

Supongamos el siguiente código de ejemplo:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. use lib "/home/$USER/lib/perl/5.10.0";
  2. use strict;
  3. use Proc::Killall;
  4.  
  5.  
  6. my $tiempo_limite = 5; # Segundos
  7. eval{
  8.    $SIG{ALRM} = "Out of time!";
  9.    alarm $tiempo_limite;
  10.    system("./ejecutable_uno");
  11.    system("./ejecutable_dos");
  12.    alarm 0;
  13. }
  14.  
  15. $SIG{CHLD} = 'IGNORE';    #Importante, esto evita que queden
  16.                           # como zombis al intentar "matarlos"
  17. killall('KILL', 'ejecutable'); #killall(SEÑAL, Expr)
  18.  
  19. die "Fuera de tiempo" if($@ =~ /Out of time/);
  20. print "Success\n";
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Todo esto suponiendo que el script anterior se ejecuta una cantidad enorme de veces y que además "ejecutable_uno" y "ejecutable_dos" son proporcionados por terceros, es decir, no sabemos si se demoran 2 segundos o 3 horas en terminar.

Espero haya servido de ayuda.
¡Saludos!

Re: Alternativa a system()

NotaPublicado: 2009-07-26 05:52 @286
por explorer
Muchísimas gracias por tu aportación, kondenado.

Solo unos comentarios...

Para instalar módulos, lo normal es usar el sistema de paquetería del sistema operativo, el comando cpan (o cpanp, más moderno), y, finalmente, la instalación como la que indicas, paso a paso.

Es claro que si no tienes privilegios de administrador, debes seguir los pasos que has puesto. Otro tema es que tampoco tengas permiso para acceder a la información de los procesos del sistema.

Por último, indicar que killall llama internamente a kill, pero, desde luego, para eso están los módulos: para simplificar nuestros procesos.