En esta sección estudiaremos varios mecanismos de comunicación entre procesos: Pipes para comunicar procesos emparentados, FIFOs para comunicar procesos no emparentados pero dentro de la misma máquina, colas de mensajes (menos relevantes), semáforos, memoria compartida, y UNIX-domain sockets.
Pipes
Una pipe provee una mecanismo de comunicación
unidireccional entre dos procesos con un ancestro común que crea la
pipe.
Una pipe ofrece un par de descriptores especiales que, en
lugar de
conectar a un archivo, conecta a otro proceso.
Una pipe provee un canal de comunicación
unidireccional entre dos procesos que
cooperan.
Por ejemplo: considere la siguiente secuencia,
% ps -ef > out1
% fgrep netscape out1 > out2
% fgrep -v agv out 2 > out3
% lp out3
% rm out1 out2 out3
fgrep fue escrito como un filtro; es decir, puede
leer desde la entrada estándar o archivo y escribe en la salida
estándar. Estos programas pueden ser unidos a través de
pipes por el shell.
% ps -ef | fgrep netscape | fgrep -v
agv | lp
Por no usar archivos intermedios, esta última forma de ejecutar la secuencia es más eficiente.
La herramienta usada para conectar estos programas
es pipe.
Creación de pipes
Hay dos formas para crear un
pipe entre dos procesos: popen()
y pipe()
La forma más simple es a través de la
función popen.
#include <stdio.h>
FILE *popen (const char * command,
const char *type),
Esta función crea una pipe
para leer desde o escribir a otro programa ejecutable. El programa
ejecutable se pasa como
argumento y podría ser un utilitario del sistema. Type puede ser "r" o
"w". Usar "r" o "w" según la operación a efectuar con el objeto
retornado por popen.
Esta función crea un nuevo
proceso y ejecuta el comando. Luego crea una pipe desde la entrada o
salida estándar de este proceso dependiendo de si se
abrió para escritura o lectura.
Para cerrar la pipe use:
#include <stdio.h>
int pclose(FILE * stream);
Esta función ejecuta un waitpid para esperar por el término del comando y su código de término.
Ejemplo: Impresión de los días de la semana con popen.
Precauciones:
Uso más avanzado de pipe
(Ver también material
aportado
por César León)
Una pipe puede ser creada con la
función pipe
#include <unistd.h>
int pipe (int f [2]);
Se crean dos descriptores de
archivo, uno desde donde el proceso lee -f[0]- (input) y otro donde el proceso escribe -f[1]- (output).
Lo que se escribe en fd[1] puede ser leído desde fd[0]. Para que éstas operaciones sean útiles, las deben relizar procesos distintos.
Condiciones especiales:
Paréntesis:
función dup2
#include
<unistd.h>
int dup (int
fd);
/*
crea una copia del escriptor fd en otra entrada de la tabla de
descriptores */
int dup2(int
fd_source, int
fd_dest);
/* equivale a: tabla[fd_dest] = tabla[fd_source]; */
dup retorna
un nuevo descriptor de archivo que referencia el mismo dispositivo que
fd.
dup2 causa que
el descriptor de archivo fd_dest se refiera al mismo contenido que
fd_source.
Otros ejemplos de uso de pipe: Programa para jugar con gnupot. Envío de correo usando cuenta gmail. Ver archivo usado (debe ser editado) para correrlo como pipegmail < pipegmailData.txt. Este programa se debe revisar pues Google suele cambiar su mecanismo de acceso dando cada vez más seguridad al usuario. GNUPLOT es un programa de línea de comandos y código abierto, muy útil para hacer todo tipo de gráficos.
FIFOs
Una
limitación de pipes es que sólo se pueden usar entre
procesos relacionados (padre-hijo etc).
FIFOs usan una entrada en el
sistema de archivo para permitir que dos procesos se relacionen con
el mismo mecanismo de comunicación entre proceso. Así FIFOs pueden ser
usadas por
procesos no relacionados (pero deben tener acceso al mismo archivo).
Las FIFOs son un
tipo especial de archivo. La creación de un FIFO es similar a la
creación de un archivo.
Creación de FIFOs
#include
<sys/types.h>
#include
<sys/stat.h>
int mkfifo (const char *path, mode_t mode); /* existente como llamado al sistema y como comando del sistema (se usa sólo mkfifo en este último caso) */
path
es el nombre de la FIFO a
ser creada en el sistema de archivo, la cual no debe existir
previamente.
mode
contiene los permisos de acceso a la
FIFO por parte de otros usuarios, es similar a su uso en la función
open().
Escrituras sobre una FIFO que
no tiene proceso lector genera una señal SIGPIPE.
Ejemplo: mkfifo también
existe como comando de consola. En este caso supongamos que deseamos
procesar la salida de un programa de dos formas, cada una con un
programa distinto. Podemos usar el comando tee para duplicar el
contenido de la salida estándar en un archivo, que en lugar de irse a
disco irá a una FIFO.
% mkfifo fifo1
% prog3 < fifo1 &
% prog1 < infile | tee fifo1 | prog2
/-------\
/------\ /----> fifo1 ----> | prog3 |
infile ----->| prog1 |----> =======
\------/ \----------------->| prog2 |
\-------/
prog3 quedara esperando en
background por datos desde fifo1.
prog1 lee infile, lo procesa y su salida es duplicada por tee hacia la
entrada de fifo1 y la salida estándar que es conducida a prog2.
Así, sin hacer uso de archivos temporales, hemos podido enviar la
salida de
prog1 a prog2 y prog3.
Ejemplo de uso pasando información de una proceso a otro. Servidor / cliente
Sockets del Dominio UNIX
(UNIX-Domain Sockets)
Son similares
a las FIFO. La dirección es dada por un archivo en el sistema de
archivo.
Su principal
diferencia con FIFO es la manera como la comunicación es
generada. En este caso se usan llamados similares a los usados para
establecer comunicación entre procesos remotos (fuera de la
máquina). Esta forma de comunicación entre procesos la
veremos cuando se vea comunicación entre procesos remotos.Como este
tipo socket opera entre proceso de una misma máquina, los mecanismos
para superar errores no son requeridos.