		TEMA IX
		PERL y Programacin de Sistemas.


PERL se puede utilizar para manipular recursos del sistema operativo, con
la misma filosofa de lenguaje C.

Muchas de las llamadas a UNIX estn presentes en PERL.


+ Procesos.

	+) Obtencin de ID de proceso.
	No existe como tal una llamada dentro de PERL, pero se puede
	utiliza la variable especial $$.


	+) Creacin de un nuevo proceso.

	Se utiliza la funcin fork. Esta regresa el id del proceso hijo, o
	cero (si es el hijo) o un error.

#!/opt/LWperl/bin/perl


$pid= fork();

if ($pid < 0) {
	die "error en fork: $!";

} elsif ( $pid == 0) {
	print "Soy proceso hijo $$ \n";
	exit 0;
} else {
	print "Proceso $$ padre creo hijo con PID $pid\n";
	exit 0;
}

	+)Esperar que termine un proceso hijo.

	En PERL existe la funcin wait.

#!/opt/LWperl/bin/perl


for ($i=0; $i<10;$i++) {
	$pid= fork();

	if ($pid < 0) {
		die "error en fork: $!";

	} elsif ( $pid == 0) {
		print "Soy proceso hijo $$ \n";
		exit 0;
	} else {
		print "Proceso $$ padre creo hijo con PID $pid\n";
	}

}

for ($i=0; $i<10;$i++) {
	print "Esperando ...\n";
	print "Hijo",wait,"concluyo con estatus $?\n";
}

	+) Esperar por un PID especifico.
	La llamada waitpid permite indicar que proceso esperar.

#!/opt/LWperl/bin/perl


for ($i=0; $i<10;$i++) {
	$pid= fork();

	if ($pid < 0) {
		die "error en fork: $!";

	} elsif ( $pid == 0) {
		print "Soy proceso hijo $$ \n";
		exit 0;
	} else {
		print "Proceso $$ padre creo hijo con PID $pid\n";
		$procesos[$i]=$pid;
	}

}

foreach $pid  (reverse @procesos) {
	print "Esperando PID $pid...\n";
	print "Hijo $pid concluyo con estatus $?\n" if waitpid($pid,0);
}

	+) Concluir un proceso.
	La llamada exit permite indicar que el proceso debe concluir.

#!/opt/LWperl/bin/perl


for ($i=0; $i<10;$i++) {
	$pid= fork();

	if ($pid < 0) {
		die "error en fork: $!";

	} elsif ( $pid == 0) {
		print "Soy proceso hijo $$ \n";
		exit $i;
	} else {
		print "Proceso $$ padre creo hijo con PID $pid\n";
		$procesos[$i]=$pid;
	}

}

foreach $pid  (reverse @procesos) {
	print "Esperando PID $pid...\n";
	print "Hijo $pid concluyo con estatus ",$?>>8,"\n" if waitpid($pid,0);
}

	+) Ejecutar un programa.
	La llamada exec permite ejecutar un programa.
	Esta llamada recibe como argumentos, una lista, con el primer
	elemento igual al comando a ejecutar, y seguido de los 
	argumentos del programa.
#!/opt/LWperl/bin/perl


$pid=fork();

if ($pid <0 ) {


} elsif ($pid == 0) {
	exec("/bin/ls","-l") || die "No puede ejecutar comando $!";

} else {
	wait;
}

El siguiente programa implanta un shell.

#!/opt/LWperl/bin/perl


print "mishell)";
while (<STDIN>) {


	chomp;

	exit(0) if /exit/;
	@comando=join(' ',$_);

	$pid=fork();

	die "No es posible arrancar nuevo comando " if $pid <0  ;
		
	if ($pid == 0) {
		exec(@comando) || die "No puede ejecutar comando $!";
	} else {
		wait;
		print "mishell)";
	}
}

	

	
+ Entrada/Salida.

En PERL la entrada/salida se manipula con un concepto denominado asas o
handle.
De hecho, STDIN es un handle que se encarga de manipular la entrada
estandar. STDOUT es para salida estndar y STDERR es el error estndar.

Un handle se puede definir como el nombre con el cual se manipula una conexin
de Entrada/Salida en un programa PERL. (El equivalente a un descritor de 
archivos).

El nombre de un handle est compueto por caracteres alfanumricos. Se recomienda
que se ponga dicho nombre con maysculas.

	+) Crear un archivo.

	La llamada open permite crear un archivo.

#!/opt/LWperl/bin/perl

print STDOUT "nombre del archivo a crear\n";

chomp($archivo=<STDIN>);

open(MIARCHIVO,">$archivo") || die "no puedo crear archivo $archivo:$!\n";

print STDOUT "archivo creado\n";
close(MIARCHIVO);

Al poner como segundo parmetro de la llamada open la expresin >$archivo,
se est indicando que cree un nuevo archivo (o lo destruya si ya existe).


	+) Abrir un archivo.
	La llamada open, sin ningn operador en el segundo parmetro (el
nombre del archivo) permite abrir un archivo.

#!/opt/LWperl/bin/perl

print STDOUT "nombre del archivo a abrir\n";

chomp($archivo=<STDIN>);

open(MIARCHIVO,"$archivo") || die "no puedo abrir archivo $archivo:$!\n";

print STDOUT "archivo abierto\n";
close(MIARCHIVO);

Si el operador es >> indica abrir el archivo al final del mismo.

	+) Cerrar un archivo

	La llamada close permite cerrar un archivo, manipulado por el
handle indicado.

	+) Lectura y escritura de un archivo.
	Existen dos mtodos para leer y escribir a un archivo.
	
	El primero es usando el operador diamante <>. Como se ha manejado,
es con STDIN. Para leer algo de un archivo, se pone entre el operador diamante
el nombre del handle del archivo. Este retorna una cadena de caracteres
terminada con una nueva lnea.
	Para escribir se puede utilizar la funcin print, la cual tiene
como primer parmetro el handle del archivo.

	El siguiente ejemplo copia un archivo a otro.

#!/opt/LWperl/bin/perl

die "Ejemplo9.pl arch1 arch2\n" if (@ARGV < 2) ;

$arch1=$ARGV[0];
$arch2=$ARGV[1];

open(ARCHIVO1,"$arch1") || die "no puedo abrir archivo $arch1:$!\n";
open(ARCHIVO2,">$arch2") || die "no puedo crear archivo $arch2:$!\n";

while (<ARCHIVO1>) {
	print ARCHIVO2 $_;

}

close(ARCHIVO1);
close(ARCHIVO2);
exit(0);

El otro mtodo es utilizando la llamada sysread y syswrite.

#!/opt/LWperl/bin/perl

die "Ejemplo9.pl arch1 arch2\n" if (@ARGV < 2) ;

$sz=128;
$arch1=$ARGV[0];
$arch2=$ARGV[1];

open(ARCHIVO1,"$arch1") || die "no puedo abrir archivo $arch1:$!\n";
open(ARCHIVO2,">$arch2") || die "no puedo crear archivo $arch2:$!\n";

while (  $sz=sysread(ARCHIVO1,$buffer,$sz)
	|| die "fin o error en read:$!\n" ) {
	print "Escribiendo $sz bytes\n";
	syswrite(ARCHIVO2,$buffer,$sz,0);

}

close(ARCHIVO1);
close(ARCHIVO2);
exit(0);

Tambin se puede utilizar la llamada read y write.

	+) Obteniendo informacin de un archivo.

	Con la llamada stat se puede lograr esto.

#!/opt/LWperl/bin/perl

die "Ejemplo11.pl arch1 \n" if (@ARGV < 1) ;

$arch1=$ARGV[0];

open(ARCHIVO1,"$arch1") || die "no puedo abrir archivo $arch1:$!\n";

($dev,$inode,$mode,$nlink,$uid,$gid,$rdev,$size,$stime,$mtime,$ctime,$blksz,$blocks)=stat(ARCHIVO1);

print '$dev $inode $mode $nlink $uid $gid $rdev $size $stime $mtime $ctime $blksz $blocks',"\n";
print join("\t",($dev,$inode,$mode,$nlink,$uid,$gid,$rdev,$size,$stime,$mtime,$ctime,$blksz,$blocks)),"\n";

close(ARCHIVO1);
exit(0);


	+) Removiendo un archivo.

	La llamada unlink permite hacer esto.

#!/opt/LWperl/bin/perl

die "Ejemplo12.pl arch1 \n" if (@ARGV < 1) ;

$arch1=$ARGV[0];


unlink($arch1) || die "error al borrar $arch1:$!\n";
exit(0);

	+) Renombrar un archivo.

	Con la llamada rename se puede hacer esta operacin.

#!/opt/LWperl/bin/perl

die "Ejemplo13.pl arch1 arch2\n" if (@ARGV < 2) ;

$arch1=$ARGV[0];
$arch2=$ARGV[1];

rename($arch1,$arch2) || die "No se puede renombrar $arch1 como $arch2:$!\n";

exit(0);

	+) Ligas duras y simblicas.

	Con la llamada link, es posible crear una liga dura.
	La llamada symlink crea una liga suave.

#!/opt/LWperl/bin/perl

print "Creando liga dura...\n"; 
die "Error en link $!\n"  unless link("Ejemplo14.pl","ldura");

print "Creando liga suave...\n"; 
die "Error en link $!\n" unless symlink("Ejemplo14.pl","lsuave");

exit(0);

	+) Creacin de directorios.

	La llamada mkdir crea un directorio

#!/opt/LWperl/bin/perl
@dirs1=qw(sdir1.1 sdir1.2 sdir1.3);
@dirs2=qw(sdir2.1 sdir2.2 sdir2.3);


foreach $dir ( qw(dir1 dir2) ) {
	print "Creando directorio $dir\n";
	mkdir($dir,0700) || die "Error al crear $dir:$!\n";

	@dirs=@dirs1 if $dir eq "dir1";
	@dirs=@dirs2 if $dir eq "dir2";

	foreach $subdir (  @dirs ) {
		print "Creando subdirectorio $dir/$subdir\n";
		mkdir("$dir/$subdir",0700) || die "Error al crear $dir/$subdir:$!\n";

	}

}
exit(0);

	+) Remover directorios.

#!/opt/LWperl/bin/perl
@dirs1=qw(sdir1.1 sdir1.2 sdir1.3);
@dirs2=qw(sdir2.1 sdir2.2 sdir2.3);


foreach $dir ( qw(dir1 dir2) ) {

	@dirs=@dirs1 if $dir eq "dir1";
	@dirs=@dirs2 if $dir eq "dir2";

	foreach $subdir (  @dirs ) {
		print "Borrando subdirectorio $dir/$subdir\n";
		rmdir("$dir/$subdir") || die "Error al borrar $dir/$subdir:$!\n";

	}
	print "borrando directorio $dir\n";
	rmdir($dir) || die "Error al borrar $dir:$!\n";

}
exit(0);

	+) Leer el contenido de un directorio

#!/opt/LWperl/bin/perl

sub leedirectorio {

	my($directorio)=$_[0];
	my($archivo);
	
	opendir(DIR,$directorio) || print "Error al leer $directorio $!\n";

	foreach $archivo ( readdir(DIR) ) {
		print "$archivo\n";

	}

	closedir(DIR);
}


if ( @ARGV <1 ) {
	die "Ejemplo17.pl dir\n";
}

 $inicio = $ARGV[0] ;

 leedirectorio($inicio);

exit(0);



+ Comunicacin entre procesos (tuberas y seales)

	+) Creacion de tuberias.

	Con la llamada pipe se crea una tubera. Esta llamada recibe
como parmetros, los handles de archivo para el extremo de lectura y
escritura.
	Se deben seguir las reglas de los pipes, quien escribe, debe cerrar
el handle de lectura, y quien lee, debe cerrar el handle de escritura.

#!/opt/LWperl/bin/perl


pipe(PIPEIN,PIPEOUT) || die "Error al abrir pipe";

$pid= fork();

if ($pid < 0) {
	die "error al crear pipe $!\n";
} elsif ($pid == 0) {

	#proceso hijo
	close(PIPEOUT) || die "$$ error al cerrar el pipe $!\n";

	$mensaje=<PIPEIN>;

	$mensaje=~tr/[a-z]/[A-Z]/;

	print "Hijo $$ procesa $mensaje\n";
	
	close(PIPEIN);
	
} else {
	#proceso padre
	close(PIPEIN) || die "$$ error al cerrar el pipe $!\n";
	print "Padre $$ escribiendo mensaje\n";
	print PIPEOUT " hola";

	close(PIPEOUT);
	wait();
}

exit(0);

Se pueden utilizar mas de dos pipes  para obtener el resultado.

#!/opt/LWperl/bin/perl


pipe(PIPEIN1,PIPEOUT1) || die "Error al abrir pipe1";
pipe(PIPEIN2,PIPEOUT2) || die "Error al abrir pipe2";

$pid= fork();

if ($pid < 0) {
	die "error al crear pipe $!\n";
} elsif ($pid == 0) {

	#proceso hijo
	close(PIPEOUT1) || die "$$ error al cerrar el pipe $!\n";
	close(PIPEIN2) || die "$$ error al cerrar el pipe $!\n";

	$mensaje=<PIPEIN1>;
	close(PIPEIN1);

	$mensaje=~tr/[a-z]/[A-Z]/;

	print "Hijo $$ procesa $mensaje\n";

 	print PIPEOUT2 $mensaje;
	close(PIPEOUT2);

	print "Hijo $$ concluye procesamiento\n";
	
} else {
	#proceso padre
	close(PIPEIN1) || die "$$ error al cerrar el pipe1 $!\n";
	close(PIPEOUT2) || die "$$ error al cerrar el pipe2 $!\n";

	print "Padre $$ escribiendo mensaje\n";
	print PIPEOUT1 "hola";
	close(PIPEOUT1);

 	$resultado= <PIPEIN2>;
	close(PIPEIN2);
	
	print "Padre $$ Respuestas es:",$resultado,"\n";

}

exit(0);

	+) FIFO o Named Pipe.
	PERL no ofrece una llamada directa para crear un named pipe,
pero se puede invocar al comando mknod (o mkfifo), para relaizarlo.

Efectuada la creacion, el named pipe se puede abrir para lectura o
escritura, con el comportamiento esperado, se queda bloqueada la llamada
open hasta que alguien abra de escritura o lectura al named pipe.


#!/opt/LWperl/bin/perl

print "creando un named pipe";

$FIFO="fifoin";
unlink $FIFO;
system('mknod',$FIFO,'p') && die "no se puede crear $FIFO: $!\n";

open(FIFO,"> $FIFO");
print FIFO "hola amigo\n";
close FIFO;
exit(0);


	+) Seales.

	PERL maneja seales, utilizando un hash. Esta estructura hash contiene
como llaves la lista de todas las seales que se pueden procesar. El nombre
de esta estructrua es %SIG

	El siguiente programa cacha un CTRL-C

#!/opt/LWperl/bin/perl

sub anulaCtlC {

	my($signame)= $_[0];

	print STDERR "Signal $signame se recibio";


}

$SIG{INT}="anulaCtlC";

while (1) {
	;
}



+ Coordinacin entre procesos (IPCs)

+ Comunicacin entre procesos remotos (Sockets)

PERL maneja sockets con una filosofa idntica a lenguaje C.

