Pasar al contenido principal

Práctica 4

Versión para impresiónSend by email

Esta práctica nos muestra el uso de tuberías para comunicación y sincronización entre procesos.
Los conceptos importantes son:

  • Se crea un tubo que comparten dos procesos (padre e hijo) a través del cual se envían mensajes.
  • Los tubos no tienen por qué tener sólo 2 extremos, cada hijo que se cree genera una nueva boca en la tubería por la que recibirá o enviará.
  • Mientras la tubería está abierta, si lees de ella (con un read) y no se ha escrito nada, quedas a la espera. Si se cierra y lees de ella, te devolverá un error (-1) en lugar de un número positivo de caracteres leidos).
  • Cuando un proceso termina, se cierran todos sus descriptores de la tubería (su boca).
  • La entrada del tubo es donde leo (igual que el teclado es la entrada estándar)
  • La salida al tubo es donde escribo (igual que la pantalla es la salida estándar)
     

ppipe1.c - Ejemplo de tuberías donde el padre envía datos al hijo y éste los imprime por pantalla.

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

/*
 * productor - Metodo que escribe 10 numeros en el id especificado 
 * por el parametro
 *
 * @param esc - Descriptor en el que imprimiremos
 *
 */
void productor(int esc) {
  int i;
  for (i=1; i<=10; i++) {                // del 1 al 10
    write(esc, &i, sizeof i);            // imprimo en esc el numero que toca
    sleep(1);                            // esperando un segundo
  }
  exit(0);                                // y salimos
}

/*
 * consumidor - Metodo que lee del id especificado por el parametro
 *
 * @param lec - Descriptor en el que leeremos
 *
 */
void consumidor(int lec) {
  int leidos, i;
  while ((leidos = read(lec, &i, sizeof i)) > 0)       // Mientras leamos algo
    printf("Recibido %d\n", i);                        // imprimimos lo leido
  exit(0);                                             // y nos salimos
}

/*
 * main
 *
 */
int main(void) {
  int pid;
  int tubo[2];        // id

  pipe(tubo);                  // se crea un pipe, se guardan los id de entrada y salida en el array de enteros

  if ((pid= fork())==0) {      // se crea un proceso y se asigna el numero a pid. Proceso hijo
    close(tubo[1]);            // cierra la salida al tubo hijo
    consumidor(tubo[0]);       // y lanza 'consumidor' con la entrada del tubo hijo
  }
  else {                       // Proceso padre
    close(tubo[0]);            // cierra la entrada del tubo padre
    productor(tubo[1]);        // t lanza 'productor' con la salida al tubo padre
  }

} 

Ejecución:

merlin1@merlins1:~/Escritorio/Practicas/4$ ls
ppipe1.c  ppipe2.c  ppipe3.c
merlin1@merlins1:~/Escritorio/Practicas/4$ gcc ppipe1.c -o ppipe1
merlin1@merlins1:~/Escritorio/Practicas/4$ ./ppipe1
Recibido 1
Recibido 2
Recibido 3
Recibido 4
Recibido 5
Recibido 6
Recibido 7
Recibido 8
Recibido 9
Recibido 10
merlin1@merlins1:~/Escritorio/Practicas/4$

 

ppipe2.c - Ejemplo de tuberías donde dos procesos hijos envía y reciben por tubería mientras el padre espera a su finalización, pero no está correcto, ya que se tienen 3 tubos!!!

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

void productor(int esc) {
  int i;
  for (i=1; i<=10; i++) {
    write(esc, &i, sizeof i);
    sleep(1);
  }
  exit(0);
}

void consumidor(int lec) {
  int leidos, i;
  while ((leidos = read(lec, &i, sizeof i)) > 0) 
    printf("Recibido %d\n", i);
  exit(0);
}

int main(void) {
  int pid; 
  int tubo[2];

  pipe(tubo);

  if ((pid= fork())==0) {   // hijo 1
    close(tubo[1]);         // cierra la salida
    consumidor(tubo[0]);    // y llama a consumidor con la entrada
  }
  else {
    close(tubo[0]);         // cierra la entrada del tubo padre!!!
    if ((pid= fork())==0) {  // hijo 2
       productor(tubo[1]);  // llama a productor con la salida
    }
  }
  wait(NULL);               // se espera a la muerte de un hijo
  wait(NULL);               // y del otro
}

 

Ejecución:

merlin1@merlins1:~/Escritorio/Practicas/4$ gcc ppipe2.c -o ppipe2
merlin1@merlins1:~/Escritorio/Practicas/4$ ./ppipe2
Recibido 1
Recibido 2
Recibido 3
Recibido 4
Recibido 5
Recibido 6
Recibido 7
Recibido 8
Recibido 9
Recibido 10
^C
merlin1@merlins1:~/Escritorio/Practicas/4$

Fijándonos, hemos tenido que matar el proceso padre (ctrl + c, que se muestra como ^C), porque existen 3 tuberías. El consumidor no deja de leer del productor porque el productor no ha cerrado todos los descripto 

 

Resultados Pedidos:

Explicación del funcionamiento de ambos programas

ppipe1.c - Se crea un pipe y después se crea un hijo. El padre cierra el descriptor de entrada del tubo y escribe cada segundo del 1 al 10 en el de salida y se muere. El hijo cierra el descriptor de salida y lee del de entrada hasta que recibe un número de caracteres igual o menor que cero (nada o error).

ppipe2.c - Se crea un pipe y dos hijos. Uno produce y el otro consume. El padre queda a la espera de la terminación de ambos hijos. El productor termina (se cierran sus descriptores), pero el consumidor sigue leyendo, quedando a la espera, porque la tubería no ha sido cerrada por el padre, por lo que se queda el programa parado.

Modificación de pipe2.c, pipe3.c que termine, funcionando correctamente.

ppipe3.c - Ejemplo de tuberías donde dos procesos hijos envía y reciben por tubería mientras el padre espera a su finalización.

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

void productor(int esc) {
  int i;
  for (i=1; i<=10; i++) {
    write(esc, &i, sizeof i);
    sleep(1);
  }
  exit(0);
}

void consumidor(int lec) {
  int leidos, i;
  while ((leidos = read(lec, &i, sizeof i)) > 0) 
    printf("Recibido %d\n", i);
  exit(0);
}

int main(void) {
  int pid;
  int tubo[2];
  pipe(tubo);				// creo el tubo
  if ((pid= fork())==0) {	// FORK - Proceso Hijo
    close(tubo[1]);			// Cierro la entrada
    consumidor(tubo[0]);	// Llamo a 'consumidor' con la entrada
  }
  else {			// Proceso Padre
       close(tubo[0]);		
    if ((pid= fork())==0) { 	// FORK - Proceso Hijo
       productor(tubo[1]);		// Llamo a 'productor' con la salida al tubo
    } else {					// Proceso Padre
       close(tubo[1]);          // Cierro la salida tambien.
    }
  }

  wait(NULL);
  wait(NULL);
}				        				        					 

Ejecución:

merlin1@merlins1:~/Escritorio/Practicas/4$ gcc ppipe3.c -o ppipe3
merlin1@merlins1:~/Escritorio/Practicas/4$ ./ppipe3
Recibido 1
Recibido 2
Recibido 3
Recibido 4
Recibido 5
Recibido 6
Recibido 7
Recibido 8
Recibido 9
Recibido 10
merlin1@merlins1:~/Escritorio/Practicas/4$

 

AdjuntoTamaño
ppipe3.c830 bytes