Como comunicar 2 procesos emparentados Mediante una pipe

  • Slides: 27
Download presentation
Como comunicar 2 procesos emparentados Mediante una pipe sin nombre

Como comunicar 2 procesos emparentados Mediante una pipe sin nombre

Que queremos hacer… • Lo mismo que haría la Shell al hacer…. # ps

Que queremos hacer… • Lo mismo que haría la Shell al hacer…. # ps –A |grep [COMANDO] • Donde [COMANDO] será un texto que buscaremos en la salida del comando ps.

¿Que hace la Shell al hacer esto? shell fork ps -A fork grep •

¿Que hace la Shell al hacer esto? shell fork ps -A fork grep • El proceso que ejecutará ps escribe sus datos en la salida std (canal 1) • Hay que redireccionar la salida std del proceso que hará el ps • El proceso que ejecutará el grep, lee sus datos de la entrada std (canal 0) • Hay que redireccionar la entrada std del proceso que hará el grep

Que necesitamos • 2 procesos (emparentados, sino no se pueden usar pipes sin nombre)

Que necesitamos • 2 procesos (emparentados, sino no se pueden usar pipes sin nombre) • 1 pipe sin nombre • Como es solo para estos procesos, no tiene sentido crear una pipe con nombre, pero también se podría usar • La pipe la usarán el proceso ps y el grep hay que crearla antes que cualquiera de los dos procesos para que la hereden

PASO 1: Esquema de procesos (simplificado) void main(int argc, char *argv[]) { int pid_ps,

PASO 1: Esquema de procesos (simplificado) void main(int argc, char *argv[]) { int pid_ps, pid_grep; if (argc!=2) usage(); pid_ps=fork(); if (pid_ps==0){ // El hijo mutará a ps execlp("ps", "-A", (char *)NULL); error_cs("Fallo al mutar a ps"); }else if (pid_ps<0) error_cs("Error en el primer fork (PS)"); pid_grep=fork(); if (pid_grep==0){ // El hijo mutará a grep execlp("grep", argv[1], (char *)NULL); error_cs("Error al mutar a grep"); }else if (pid_grep<0) error_cs("Error en el segundo fork(GREP)"); while(waitpid(-1, NULL, 0)>0); // El padre simplemente esperará }

PASO 2: Creamos la pipe void main(int argc, char *argv[]) { int pid_ps, pid_grep,

PASO 2: Creamos la pipe void main(int argc, char *argv[]) { int pid_ps, pid_grep, p[2]; Necesitamos un vector de 2 enteros if (argc!=2) usage(); pipe(p); // Creamos la pipe antes del ps y el grep pid_ps=fork(); if (pid_ps==0){ // El hijo mutará a ps execlp("ps", "-A", (char *)NULL); error_cs("Fallo al mutar a ps"); }else if (pid_ps<0) error_cs("Error en el primer fork (PS)"); pid_grep=fork(); if (pid_grep==0){ // El hijo mutará a grep execlp("grep", argv[1], (char *)NULL); error_cs("Error al mutar a grep"); }else if (pid_grep<0) error_cs("Error en el segundo fork(GREP)"); while(waitpid(-1, NULL, 0)>0); // El padre simplemente esperará } P[0] tendrá el canal de lectura y p[1] el de escritura

 • El proceso ps escribirá en el canal 1 • El canal 1

• El proceso ps escribirá en el canal 1 • El canal 1 debe estar vinculado a la pipe PASO 3: Redireccionar canales(ps) … ps if (pid_ps==0){ // El hijo mutará a ps dup 2(p[1], 1); execlp("ps", "-A", (char *)NULL); error_cs("Fallo al mutar a ps"); }else if (pid_ps<0) error_cs("Error en el primer fork (PS)"); … • Si el canal 1 estaba activo (lo normal), primero se cierra • Después se copia el contenido de la tabla de canales de la entrada p[1] en la entrada 1 1

PASO 4: cerrar canales(ps) … • El proceso ps solo necesita el canal 1

PASO 4: cerrar canales(ps) … • El proceso ps solo necesita el canal 1 ps if (pid_ps==0){ // El hijo mutará a ps dup 2(p[1], 1); close(p[0]); close(p[1]); execlp("ps", "-A", (char *)NULL); error_cs("Fallo al mutar a ps"); }else if (pid_ps<0) error_cs("Error en el primer fork (PS)"); … Antes de mutar!!!!!! 1

 • El proceso grep leerá en el canal 0 • El canal 0

• El proceso grep leerá en el canal 0 • El canal 0 debe estar vinculado a la pipe PASO 3: Redireccionar canales(grep) … 0 grep if (pid_grep==0){ // El hijo mutará a grep dup 2(p[0], 0); execlp(“grep", argv[1], (char *)NULL); error_cs("Fallo al mutar a grep"); }else if (pid_grep<0) error_cs("Error en el segundo fork (GREP)"); … • Si el canal 0 estaba activo (lo normal), primero se cierra • Después se copia el contenido de la tabla de canales de la entrada p[0] en la entrada 0

PASO 4: cerrar canales(grep) … • El proceso grep solo necesita el canal 0

PASO 4: cerrar canales(grep) … • El proceso grep solo necesita el canal 0 0 grep if (pid_grep==0){ // El hijo mutará a grep dup 2(p[0], 0); close(p[0]); close(p[1]); execlp(“grep", argv[1], (char *)NULL); error_cs("Fallo al mutar a grep"); }else if (pid_grep<0) error_cs("Error en el segundo fork (GREP)"); … Antes de mutar!!!!!!

PASO 5: cerrar canales (padre) void main(int argc, char *argv[]) { int pid_ps, pid_grep,

PASO 5: cerrar canales (padre) void main(int argc, char *argv[]) { int pid_ps, pid_grep, p[2]; if (argc!=2) usage(); pipe(p); // Creamos la pipe antes del ps y el grep pid_ps=fork(); if (pid_ps==0){ // El hijo mutará a ps ……. }else if (pid_ps<0) error_cs("Error en el primer fork (PS)"); pid_grep=fork(); if (pid_grep==0){ // El hijo mutará a grep …. . }else if (pid_grep<0) error_cs("Error en el segundo fork(GREP)"); close(p[0]); close(p[1]); El padre no usa la pipe while(waitpid(-1, NULL, 0)>0); // El padre simplemente esperará } Antes del waitpid!!!!

Probad el código!!! Veremos como afecta a las estructuras de datos

Probad el código!!! Veremos como afecta a las estructuras de datos

¿Como afecta a las estructuras de datos? § Suponed que el programa se llama

¿Como afecta a las estructuras de datos? § Suponed que el programa se llama ps_grep y lo lanzamos asi: # ps_grep bash • La entrada y salida std por defecto será la consola (“tty” en la tabla) Canal; entrada_tfo 0 0 1 0 2 0 3 4 T. Canales Proceso inicial Ent. ; refs. Modo; l/e; 0 RW 3 - Ent. T. Inodo 0 Ent. ; 0 1 1 2 2 3 4 5 Tabla Ficheros Abiertos refs. 1 Inodo; “tty”

Estado inicial: comentarios • Tenemos tres canales ocupados: 0, 1 y 2 que corresponden

Estado inicial: comentarios • Tenemos tres canales ocupados: 0, 1 y 2 que corresponden con los canales de la entrada/salida error std • Como la entrada/salida error std. Asumimos que es la consola, podemos hacer que apunte a una única entrada de la TFO • Referencias=3 porque hay tres canales en total apuntando a ella • Como es de lectura/escritura el modo es RW • Como la consola no es un fichero de datos, no ofrece un acceso secuencial a la información, por lo tanto no ponemos un valor en la posición de l/e • Usamos la entrada 0 de la tabla de inodos • En la T. inodos deberíamos poner el número de inodo, pero en los casos que tengamos información del sistema de fichero lo simplificaremos con una etiqueta tipo “consola” o “pipe”

Ejecución concurrente • Los tres procesos estará ejecutándose a la vez, las modificaciones que

Ejecución concurrente • Los tres procesos estará ejecutándose a la vez, las modificaciones que pondremos aquí corresponden a una posible secuencia • Por simplicidad asumiremos que primero se ejecuta el padre, luego añadiremos las modificaciones del proceso del ps y luego las del grep. • La tabla de canales está en el PCB, pero por simplicidad pintamos solo la tabla de canales • Cada vez que hagamos un fork: • Hemos de añadir una nueva tabla de canales • Será una réplica de la de su padre • Habrá que actualizar el contador de referencias

Secuencia del padre 1. 2. 3. 4. 5. pipe(p); fork() close(p[0]); close(p[1]); while(waitpid(-1, null,

Secuencia del padre 1. 2. 3. 4. 5. pipe(p); fork() close(p[0]); close(p[1]); while(waitpid(-1, null, 0)>0);

1. Creamos la pipe § Hemos de añadir 1 entrada en la T. Inodos,

1. Creamos la pipe § Hemos de añadir 1 entrada en la T. Inodos, 2 entradas en la TFO y 2 canales. Primero el acceso de lectura y luego el de escritura. Canal; entrada_tfo 0 0 1 0 2 0 3 1 4 2 T. Canales Proceso inicial refs. Inodo; Modo; l/e; 0 3 RW - 0 0 1 “tty” 1 1 R - 1 1 2 “pipe” 2 1 W - 1 2 3 4 5 Tabla Ficheros Abiertos Ent. T. Inodo Ent. ; refs.

2. 3. Fork: no modifica la tabla de canales del padre Ent. ; refs.

2. 3. Fork: no modifica la tabla de canales del padre Ent. ; refs. Inodo; Ent. ; refs. Modo; l/e; Ent. T. Inodo Canal; entrada_tfo Proceso 0 0 inicial 1 0 2 0 3 1 4 2 0 9 RW - 0 0 1 “tty” 1 3 R - 1 1 2 “pipe” 2 3 W - 1 2 3 4 5 Canal; entrada_tfo Proceso 0 0 “ps” 1 0 Canal; entrada_tfo Proceso 0 0 “grep” 1 0 2 0 3 1 4 2

4. Cierra los canales : close(3); close(4) Canal; entrada_tfo Proceso 0 0 inicial 1

4. Cierra los canales : close(3); close(4) Canal; entrada_tfo Proceso 0 0 inicial 1 0 2 0 3 1 4 2 Inodo; 0 9 RW - 0 0 1 “tty” 1 3 2 R - 1 1 2 “pipe” 2 3 2 W - 1 2 4 5 Canal; entrada_tfo Proceso 0 0 “ps” 1 0 refs. Modo; l/e; 3 Canal; entrada_tfo Proceso 0 0 “grep” 1 0 2 0 3 1 4 2 Ent. T. Inodo Ent. ; refs.

Secuencia ps 1. dup 2(p[1], 1); dup 2(4, 1) • Cierra el canal 1

Secuencia ps 1. dup 2(p[1], 1); dup 2(4, 1) • Cierra el canal 1 actualizar contador de referencias • Copia el canal 4 en la salida 1 actualizar contador referencias 2. close(p[0]); close(p[1]); close(3); close(4); • Actualizar contador de referencias 3. execlp(“ps", ”-A”, (char *)NULL); ü No afecta a la tabla de canales, solo es un cambio del binario

1. Cambios proceso PS: dup 2(4, 1) Canal; entrada_tfo Proceso 0 0 inicial 1

1. Cambios proceso PS: dup 2(4, 1) Canal; entrada_tfo Proceso 0 0 inicial 1 0 2 0 3 98 RW - 0 0 1 “tty” 1 2 R - 1 1 2 “pipe” 2 23 W - 1 2 5 Canal; entrada_tfo Proceso 0 0 “ps” 1 02 Inodo; 0 4 4 refs. Modo; l/e; 3 Canal; entrada_tfo Proceso 0 0 “grep” 1 0 2 0 3 1 4 2 Ent. T. Inodo Ent. ; refs.

2. Cambios proceso PS: close(3); close(4); Canal; entrada_tfo Proceso 0 0 inicial 1 0

2. Cambios proceso PS: close(3); close(4); Canal; entrada_tfo Proceso 0 0 inicial 1 0 2 0 3 8 RW - 0 0 1 “tty” 1 21 R - 1 1 2 “pipe” 2 32 W - 1 2 5 Canal; entrada_tfo Proceso 0 0 “ps” 1 2 Inodo; 0 4 4 refs. Modo; l/e; 3 Canal; entrada_tfo Proceso 0 0 “grep” 1 0 2 0 3 1 4 2 Ent. T. Inodo Ent. ; refs.

Secuencia grep 1. dup 2(p[0], 0); dup 2(3, 0) • Cierra el canal 0

Secuencia grep 1. dup 2(p[0], 0); dup 2(3, 0) • Cierra el canal 0 actualizar contador de referencias • Copia el canal 3 en la entrada 0 actualizar contador referencias 2. close(p[0]); close(p[1]); close(3); close(4); • Actualizar contador de referencias 3. execlp(“grep", argv[1], (char *)NULL); ü No afecta a la tabla de canales, solo es un cambio del binario

1. Cambios proceso grep: dup 2(3, 0); Canal; entrada_tfo Proceso 0 0 inicial 1

1. Cambios proceso grep: dup 2(3, 0); Canal; entrada_tfo Proceso 0 0 inicial 1 0 2 0 3 87 RW - 0 0 1 “tty” 1 12 R - 1 1 2 “pipe” 2 2 W - 1 2 5 Canal; entrada_tfo Proceso 0 0 “ps” 1 2 2 0 Inodo; 0 4 4 refs. Modo; l/e; 3 Canal; entrada_tfo Proceso 0 01 “grep” 1 0 2 0 3 3 1 4 4 2 Ent. T. Inodo Ent. ; refs.

2. Cambios proceso grep: close(3); close(4); Canal; entrada_tfo Proceso 0 0 inicial 1 0

2. Cambios proceso grep: close(3); close(4); Canal; entrada_tfo Proceso 0 0 inicial 1 0 2 0 3 7 RW - 0 0 1 “tty” 1 21 R - 1 1 2 “pipe” 2 21 W - 1 2 5 Canal; entrada_tfo Proceso 0 0 “ps” 1 2 2 0 Inodo; 0 4 4 refs. Modo; l/e; 3 Canal; entrada_tfo Proceso 0 1 “grep” 1 0 2 0 3 3 1 4 4 2 Ent. T. Inodo Ent. ; refs.

Este es el estado cuando el proceso padre está en el waitpid y los

Este es el estado cuando el proceso padre está en el waitpid y los hijos están haciendo el ps y el grep Canal; entrada_tfo Proceso 0 0 inicial 1 0 2 0 3 7 RW - 0 0 1 “tty” 1 1 R - 1 1 2 “pipe” 2 1 W - 1 2 5 Canal; entrada_tfo Proceso 0 0 “ps” 1 2 2 0 Inodo; 0 4 4 refs. Modo; l/e; 3 Canal; entrada_tfo Proceso 0 1 “grep” 1 0 2 3 3 4 4 0 Ent. T. Inodo Ent. ; refs.

Finalización de procesos • El primero en acabar será el ps y luego el

Finalización de procesos • El primero en acabar será el ps y luego el grep, ya que uno produce datos y el otro los lee. ¿ Como estarán las tablas cuando solo quede el padre justo después del waitpid? Canal; entrada_tfo Proceso 0 0 inicial 1 0 2 3 4 0 Ent. ; refs. Modo; l/e; 0 RW 3 - Ent. T. Inodo 0 Ent. ; 0 1 1 2 2 refs. 1 Inodo; “tty” 3 4 5 Cuando un proceso termina se cierran todos los canales que le queden abiertos