Sunteți pe pagina 1din 9

Guin para ilustrar el uso de pipes. Como con las seales, ya hemos usado pipes desde la shell.

Por ejemplo en el comando ls | sort ls escribe en su salida estandar el listado de los ficheros del directorio actual. Al escribir el operador '|', la shell conecta la salida estndar de ls con la entrada estandar de sort a travs de una pipe o tubera. La pipe actua como intermediario entre los dos comandos. Como es de esperar, a la salida, preserva el orden en el que se escribe. Adems tiene capacidad de almacenar una determinada cantidad de datos. Esto permite que ambos procesos funcionen de manera, hasta cierto punto, descoordinada. En cualquier caso, para evitar que se pierda informacin, ejerce sobre los procesos un control de flujo. De manera que el proceso que escribe es bloqueado si no hay sitio libre en la pipe. Igualmente, el proceso que lee se bloquea si no hay datos disponibles. Vamos a hacer unos experimentos con una pipe. Para crearla se usa la llamada al sistema pipe. MIRAR man 2 pipe!!! El siguiente programa crea una y escribe en ella, a continuacin lee y muestra en la salida estandar lo que se ley. #include <stdio.h> #include <unistd.h> int main() { int tub[2],i; char buffer[10]; if(pipe(tub) < 0) { perror("pipe"); return 1; } for(i=0; i<5; i++) { if(write(tub[1],"Hola pipe",10)<0) { perror("write"); return 1; } if(read(tub[0],buffer,10)<0) { perror("read"); return 1; } puts(buffer); } close(tub[0]); close(tub[1]); }

Ciertamente este programa es muy sencillo y bastante inutil. Vamos a liarla un poco. Sabemos que la pipe almacena datos, lo podremos comprobar si dividimos el bucle for en dos. De manera que escribimos todas las lneas de una vez y luego las leemos todas juntas. #include <stdio.h> #include <unistd.h> #define LINEAS 5 int main() { int tub[2],i; char buffer[20]; if(pipe(tub) < 0) { perror("pipe"); return 1; } for(i=0; i<LINEAS; i++) { sprintf(buffer,"Hola pipe %d",i); if(write(tub[1],buffer,20)<0) { perror("write"); return 1; } } for(i=0; i<LINEAS; i++) { if(read(tub[0],buffer,20)<0) { perror("read"); return 1; } puts(buffer); } close(tub[0]); close(tub[1]); } En este programa podemos apreciar que la pipe efectivamente almacena todas las lneas hasta que son leidas. La pregunta ahora es Podemos incrementar LINEAS indefinidamente? Y si tratamos de leer ms lneas de las que hemos escrito? Las pipes tienen una semntica de uso que impide que se pierdan datos. Hemos visto que sucede si se escribe ms de la cuenta o si se intenta leer cuando no hay datos. Pero qu pasa cuando se cierra alguno de los descriptores de la pipe? Podemos calcular el tamao de una pipe, con el siguiente programa. #include <stdio.h> #include <unistd.h> int main() {

int tub[2],i; if(pipe(tub) < 0) { perror("pipe"); return 1; } i=0; while(write(tub[1],"a",1) > 0) { printf("%d\n",i++); } close(tub[0]); close(tub[1]); } Hemos explorado como funciona una pipe. Pero usar una pipe desde un solo proceso no tiene mucho sentido. En realidad su utilidad principal es la comunicacin entre procesos. #include <stdio.h> #include <unistd.h> #define LINEAS 20 int tub[2]; int hijo(); int main() { int i,pid; char buffer[20]; if(pipe(tub) < 0) { perror("pipe"); return 1; } switch(pid=fork()) { case -1: perror("fork"); return 1; case 0: return hijo(); } close(tub[0]); for(i=0; i<LINEAS; i++) { sprintf(buffer,"Hola hijo %d",i); if(write(tub[1],buffer,20)<0) { perror("write"); return 1; } } close(tub[1]); } int hijo() {

int i; char buffer[20]; close(tub[1]); for(i=0; i<LINEAS; i++) { if(read(tub[0],buffer,20)<0) { perror("read"); return 1; } puts(buffer); } close(tub[0]); return 0; } Para ver en que manera el uso de la pipe descoordina ambos procesos, podemos poner unos prints antes de read y write. Si ejecutamos vemos que no hay un orden en las llamadas. Aade un sleep(1) al bucle del padre y observa lo que sucede con las escrituras y lecturas. Elimina el sleep del padre, aadeselo al bucle del hijo y observa de nuevo. Finalmente vamos a probar qu pasa si uno de los dos procesos se ejecuta cuando el otro a terminado. Para ello reduce el nmero de iteraciones que hace el padre, ejecuta e intenta explicar lo que sucede. Del mismo modo ahora haz ms pequeo el nmero de iteraciones del hijo y vuelve a ejecutar. Pasa lo mismo que antes? La implementacin de las pipes por parte del sistema operativo garantiza que las escrituras pequeas se realizan de manera atmica. Esto permite que varios procesos escriban en la pipe concurrentemente. Vamos a verlo con un ejemplo. #include <stdio.h> #include <unistd.h> #define LINEAS 10 int tub[2]; int hijo(); int main() { int i,pid; char buffer[30]; char mensaje1[30] = "me compras un pony?"; char mensaje2[30] = "me compras una excavadora?"; if(pipe(tub) < 0) { perror("pipe"); return 1; } switch(pid=fork()) { case -1: perror("fork"); return 1; case 0: return hijo(mensaje1); } switch(pid=fork())

{ case -1: perror("fork"); return 1; case 0: return hijo(mensaje2); } close(tub[1]); while((i=read(tub[0],buffer,30))>0) { puts(buffer); puts("Ya veremos..."); } if(i==-1) { perror("read"); return 1; } close(tub[0]); } int hijo(char *mensaje) { int i; close(tub[0]); for(i=0; i<LINEAS; i++) { if(write(tub[1],mensaje,30)<0) { perror("write"); return 1; } } close(tub[1]); return 0; } Por la misma propiedad tambin se puede implementar una seccin crtica con pipes. Al final del fichero fork.txt del tema 3 haba un ejemplo en el que padre e hijo escribian simultaneamente sobre un fichero. Esto daba problemas porque las escrituras no eran atomicas (como lo son en las pipes). Vamos a retomar ese ejemplo y hacer que funcione correctamente con una seccin crtica implementada con pipes. #include <unistd.h> #include <fcntl.h> #include <stdio.h> int main() { int pid; int fd; if((fd = open("test.txt",O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) { perror("open"); return 1; } pid = fork(); if(pid == -1) { perror("fork");

} else if(pid > 0) { sleep(1); write(fd,"padre: escribo a fichero 1\n",27); sleep(1); write(fd,"padre: escribo a fichero 2\n",27); } else { write(fd,"hijo: escribo en fichero 1\n",27); sleep(1); write(fd,"hijo: escribo en fichero 2\n",27); sleep(1); write(fd,"hijo: escribo en fichero 3\n",27); sleep(1); write(fd,"hijo: escribo en fichero 4\n",27); } write(fd,"voy a cerrar el fichero\n",25); if(close(fd) < 0) { perror("close"); } return 1; } Ejemplo con seccin crtica. #include <unistd.h> #include <fcntl.h> #include <stdio.h> int mut[2]; int fd; void escribir(char *msg,int cnt) { char c; read(mut[0],&c,1); write(fd,msg,cnt); write(mut[1],&c,1); } int main() { int pid; char c; if(pipe(mut) < 0) { perror("pipe"); return 1; } write(mut[1],&c,1); if((fd = open("test.txt",O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) { perror("open"); return 1; } pid = fork();

if(pid == -1) { perror("fork"); } else if(pid > 0) { escribir("padre: escribo a fichero 1\n",27); sleep(1); escribir("padre: escribo a fichero 2\n",27); } else { escribir("hijo: escribo en fichero 1\n",27); sleep(1); escribir("hijo: escribo en fichero 2\n",27); sleep(1); escribir("hijo: escribo en fichero 3\n",27); sleep(1); escribir("hijo: escribo en fichero 4\n",27); } escribir("voy a cerrar el fichero\n",25); if(close(fd) < 0) { perror("close"); } close(mut[0]); close(mut[1]); return 1; } Las pipes solo se pueden usar entre procesos emparentados ya que solo se pueden heredar. Puede un hijo crear una pipe y compartirla con si padre? Para resolver esta situacin, existen unas pipes con nombre llamadas fifos. Estas se crean con un comando especial y aparecen en el sistema de ficheros como un directorio o fichero corriente. Los procesos que quieran hacer uso de una fifo la abren como si fuese un fichero normal. Como ejemplo vamos a crear y usar una fifo desde la shell. Para ello ejecutamos el comando: mkfifo canal Podemos ver la fifo inmediatamente despues con un comando ls. ls -l Observa que el tipo de fichero (primer caracter del ls) es 'p' (pipe), pero que por lo dems parece un fichero normal. Ahora podemos escribir algo en la fifo echo hola > canal pero se bloquea porque no hay ningn lector. En el caso de las pipes, se hubiera producido una seal 'broken pipe'. En otra terminal podemos leer el contenido de la fifo con el comando cat canal

En el momento de que se comienza a leer el sistema operativo desbloquea la escritura sobre la fifo y se produce la comunicacin entre los procesos. Observa que los procesos no tienen ninguna relacin padre-hijo. Vamos a rehacer el ejemplo con dos escritores con fifos. #include <stdio.h> #include <unistd.h> #include <fcntl.h> #define LINEAS 10 int hijo(); int main() { int i,pid,fd; char buffer[30]; char mensaje1[30] = "me compras un pony?"; char mensaje2[30] = "me compras una excavadora?"; if(mkfifo("canal",0600) < 0) { perror("mkfifo"); return 1; } switch(pid=fork()) { case -1: perror("fork"); return 1; case 0: return hijo(mensaje1); } switch(pid=fork()) { case -1: perror("fork"); return 1; case 0: return hijo(mensaje2); } fd=open("canal",O_RDONLY); while((i=read(fd,buffer,30))>0) { puts(buffer); puts("Ya veremos..."); } if(i==-1) { perror("read"); return 1; } close(fd); } int hijo(char *mensaje) { int i,fd; fd=open("canal",O_WRONLY); for(i=0; i<LINEAS; i++) { if(write(fd,mensaje,30)<0)

{ perror("write"); return 1; } } close(fd); return 0; }

S-ar putea să vă placă și