A continuación se iniciará la revisión de como múltiples procesos
pueden estar siendo ejecutados en una máquina y quizás siendo controlados
(generados por la función fork()
) por uno de nuestros programas.
En numerosas aplicaciones hay una necesidad clara para que estos procesos se comuniquen para el intercambio de datos o información de control. Hay unos cuantos métodos para hacer lo anterior. Se pueden considerar los siguientes:
En este capítulo se estudia el entubamiento de dos procesos, en los siguientes capítulos se revisan los otros métodos.
El entubamiento es un proceso donde la entrada de un proceso es hecha con la
salida de otro. Se ha visto ejemplos de lo anterior en la línea de
comandos de Unix cuando se usa el símbolo |
.
Se verá como hacer lo anterior en un programa de C teniendo dos (o más) procesos divididos. Lo primero que se debe hacer es abrir una tubería, en Unix permite dos formas de hacerlo:
FILE *popen(char *orden, char *tipo)
Abre una tubería de E/S donde
el comando es el proceso que será conectado al proceso que lo esta llamando
de esta forma creando el pipe. El tipo es "r
" -- para lectura, o
"w
" para escritura.
La función popen()
regresa un apuntador a un flujo o NULL para
algún tipo de error.
Una tubería abierta con popen()
deberá siempre cerrarse con
pclose(FILE *flujo)
.
Se usa fprintf()
y fscanf()
para comunicarse con el flujo de la
tubería.
int pipe(int descf[2])
Crea un par de descritores de archivo, que
apuntan a un nodo i de una tubería, y los pone en el vector de dos
elementos apuntado por descf
. descf[0]
es para lectura y
descf[1]
es para escritura.
pipe()
regresa 0
en caso de éxito, y en caso de error se
devuelve -1
y se pone un valor apropiado en errno
.
El modelo de programación estándar es que una vez que la tubería ha
sido puesta, dos (o más) procesos cooperativos serán creados por
división y los datos serán pasados empleando read()
y write()
.
Las tuberías abiertas con pipe()
deberán ser cerradas con
close(int fd)
.
A continuación se muestra un ejemplo de entubamiento que se utiliza para estar enviando datos al programa gnuplot
, empleado para graficar. La descripción de las funciones de los módulos es la siguiente:
grafica.c
que contiene la
función main()
y graficador.c
que contiene las rutinas para
enviar los datos al programa gnuplot
.
/usr/bin
.
grafica.c
llama al programa gnuplot.
grafica.c
, en uno de
ellos se gráfica la función y=0.5 e y=aleat(0-1.0)
y en el otro las
funciones y=sen(x) e y=sen(1/x)
.
El listado del código para grafica.c
es el siguiente:
/* grafica.c - * Ejemplo de la tuberias (pipe) de Unix. Se llama al paquete "gnuplot" * para graficar desde un programa en C. * La informacion es entubada a gnuplot, se crean 2 tuberias, una dibujara la * grafica de y=0.5 e y= random 0-1.0, la otra graficara y=sen(1/x) e y=sen x */ #include "externals.h" #include <signal.h> #define DEG_TO_RAD(x) (x*180/M_PI) void salir(); FILE *fp1, *fp2, *fp3, *fp4; main() { float i; float y1,y2,y3,y4; /* abrir archivos en los cuales se guardaran los datos a graficar */ if ( ((fp1 = fopen("plot11.dat","w")) == NULL) || ((fp2 = fopen("plot12.dat","w")) == NULL) || ((fp3 = fopen("plot21.dat","w")) == NULL) || ((fp4 = fopen("plot22.dat","w")) == NULL) ) { printf("Error no se puede abrir uno o varios archivos de datos\n"); exit(1); } signal(SIGINT,salir); /* Atrapar la llamada de la funcion de salida ctrl-c */ IniciarGraf(); y1 = 0.5; srand48(1); /* poner semilla */ for (i=0;;i+=0.01) /* incrementar i siempre */ { /* usar ctrl-c para salir del programa */ y2 = (float) drand48(); if (i == 0.0) y3 = 0.0; else y3 = sin(DEG_TO_RAD(1.0/i)); y4 = sin(DEG_TO_RAD(i)); /* cargar archivos */ fprintf(fp1,"%f %f\n",i,y1); fprintf(fp2,"%f %f\n",i,y2); fprintf(fp3,"%f %f\n",i,y3); fprintf(fp4,"%f %f\n",i,y4); /* asegurarse que los buffers son copiados al disco */ /* para que gnuplot pueda leer los datos */ fflush(fp1); fflush(fp2); fflush(fp3); fflush(fp4); /* graficar alot graph */ Graf1Vez(); usleep(10000); /* dormir por un corto tiempo (250 microsegundos) */ } } void salir() { printf("\nctrl-c atrapada:\n Terminando las tuberias\n"); PararGrafica(); printf("Cerrando los archivos de datos\n"); fclose(fp1); fclose(fp2); fclose(fp3); fclose(fp4); printf("Borrando los archivos de datos\n"); BorrarDat(); }
El módulo graficador.c
es el siguiente:
/**********************************************************************/ /* modulo: graficador.c */ /* Contiene rutinas para graficar un archivo de datos producido por */ /* programa. En este caso se grafica en dos dimensiones */ /**********************************************************************/ #include "externals.h" static FILE *plot1, *plot2, *ashell; static char *iniciargraf1 = "plot [] [0:1.1] 'plot11.dat' with lines, 'plot12.dat' with lines\n"; static char *iniciargraf2 = "plot 'plot21.dat' with lines, 'plot22.dat' with lines\n"; static char *comando1= "/usr/bin/gnuplot> dump1"; static char *comando2= "/usr/bin/gnuplot> dump2"; static char *borrarchivos = "rm plot11.dat plot12.dat plot21.dat plot22.dat"; static char *set_term = "set terminal x11\n"; void IniciarGraf(void) { plot1 = popen(comando1, "w"); fprintf(plot1, "%s", set_term); fflush(plot1); if (plot1 == NULL) exit(2); plot2 = popen(comando2, "w"); fprintf(plot2, "%s", set_term); fflush(plot2); if (plot2 == NULL) exit(2); } void BorrarDat(void) { ashell = popen(borrarchivos, "w"); exit(0); } void PararGrafica(void) { pclose(plot1); pclose(plot2); } void Graf1Vez(void) { fprintf(plot1, "%s", iniciargraf1); fflush(plot1); fprintf(plot2, "%s", iniciargraf2); fflush(plot2); }
El archivo de cabecera externals.h
contiene lo siguiente:
/* externals.h */ #ifndef EXTERNALS #define EXTERNALS #include <stdio.h> #include <stdlib.h> #include <math.h> /* prototipos */
Con la finalidad de tener un mejor control en la compilación del programa se
luede usar el siguiente archivo Makefile
:
FUENTES.c= grafica.c graficador.c INCLUDES= CFLAGS= SLIBS= -lm PROGRAMA= grafica OBJETOS= $(FUENTES.c:.c=.o) $(PROGRAMA): $(INCLUDES) $(OBJETOS) gcc -o $(PROGRAMA) $(OBJETOS) $(SLIBS) grafica.o: externals.h grafica.c gcc -c grafica.c graficador.o: externals.h graficador.c gcc -c graficador.c clean: rm -f $(PROGRAMA) $(OBJETOS)