370 likes | 516 Views
Linux : Pipe. Amelia Guillén Rodríguez Michael Kokaly Kokaly. PIPE : INTRODUCCION. Transmisión de datos entre procesos a través de un canal : datos escritos en un extremo se leen en el otro extremo. Podemos comparar tuberías con una cola FIFO de caracteres. PIPE:INTRODUCCION.
E N D
Linux : Pipe Amelia Guillén Rodríguez Michael Kokaly Kokaly
PIPE : INTRODUCCION • Transmisión de datos entre procesos a través de un canal : datos escritos en un extremo se leen en el otro extremo. • Podemos comparar tuberías con una cola FIFO de caracteres.
PIPE:INTRODUCCION • Las tuberías no ofrecen comunicación estructurada. • Desventaja : lectura de datos independiente de la escritura. • Ventaja : permite leer de una sola vez los datos escritos en varias ocasiones. • La gestión de las tuberías esta integrada en el sistema de archivos. • Acceso a las tuberías por descriptores de entrada/salida : uno para lectura en la tubería y otro para la escritura en la tubería
TUBERIAS : TIPOS • Tubería anónima : • Creada por un proceso. • Transmisión de descriptores sólo por herencia hacia sus descendientes. • Mecanismo restrictivo : sólo comunicación entre procesos cuyo antecesor común es el creador de la tubería.
TUBERIAS : TIPOS • Tubería con nombre : • Existen físicamente en el sistema de archivos. • Se manipulan como archivos respecto a operaciones de apertura,cierre,lectura y escritura,por lo que no existe la restricción de las anónimas. • Para identificar un archivo tipo tubería con nombre utilizamos el atributo p del comando ls.
TUBERIAS ANONIMAS.CREACION • Llamada pipe para crear una tubería anónima. • Para poder crearla se pasan como parámetro una tabla de dos enteros : #include <unistd.h> int pipe (int filedes[2]); • Si la llamada ha sido exitosa filedes[0] contendrá el descriptor de lectura y filedes[1] contendrá el descriptor de escritura.
TUBERIAS ANONIMAS CREACION : ERRORES • La llamada pipe puede generar errores : • EFAULT : La tabla pasada como parámetro no es válida. • EMFILE : Se ha alcanzado el número máximo de archivos abiertos para el proceso actual. • ENFILE : Se ha alcanzado el número máximo de archivos abiertos en el sistema
TUBERIAS ANONIMAS • Se pueden utilizar funciones de alto nivel para operaciones de lectura y escritura : printf,scanf,sprintf… • Problemas : todo carácter escrito no está disponible en el otro extremo de inmediato ya que usan memorias intermedias.
TUBERIAS ANONIMAS • La llamada al sistema close permite cerrar descriptores inutilizados. • El proceso lector queda bloqueado hasta que lee la cantidad de datos precisa en la llamada read.
TUBERIA ANONIMA:CREACION TUBERIA-EJECUCION PROGRAMA • Una tubería puede emplearse para comunicarse con un programa externo. • Para ello dos funciones de biblioteca : #include <stdio.h> FILE *popen (const char *command, const char *type); int pclose (FILE *stream); • Popen crea una tubería y un proceso hijo. • El proceso hijo ejecuta el comando especificado por command. • Pclose cierra la tuberia (fclose) y finaliza el proceso hijo asociado
TUBERIA ANONIMA:CREACION TUBERIA-EJECUCION PROGRAMA • Popen devuelve un descriptor de entradas/salidas correspondiente a la tubería, según el valor de parámetro type: • Si type es la cadena <<r>>, el descriptor es accesible en lectura, y permite acceder a la salida estándar del mandato. • Si type es la cadena <<w>>, el descriptor es accesible en escritura, y permite acceder a la entrada estándar del mandato.
IMPLEMENTACION • Desde el punto de vista de i-nodo de una tubería, el campo i_pipe se pone a 1, lo que identifica el tipo de i-nodo. • El campo u del i-nodo está constituido, en el caso de una tubería (anónima o no), por la estructura pipe_inode_info descrita en el archivo <linux/pipe_fs_i.h>. • El tamaño de la tubería se limita a 4 KB (valor de la constante PIPE_BUF definida en el archivo <linux/limits.h>). • Este límite corresponde al valor límite para la realización de una escritura atómica en la tubería. El usuario puede escribir más datos en la tubería, pero dejará de tener garantizada la atomización de la operación.
DO_PIPE int do_pipe(int *fd) {… struct inode * inode; struct file *f1, *f2; int error; … error = -ENFILE; f1 = get_empty_filp(); if (!f1) goto no_files; f2 = get_empty_filp(); if (!f2) goto close_f1; inode = get_pipe_inode(); //creación del i-nodo de 1 tubería if (!inode) goto close_f12; error = get_unused_fd();
DO_PIPE if (error < 0) goto close_f12_inode; i = error; error = get_unused_fd(); if (error < 0) goto close_f12_inode_i; j= error; Error= -ENOMEM; sprintf(name, "[%lu]", inode->i_ino);//Escribe el número que caracteriza el inode this.name= name; … dentry = d_alloc(pipe_mnt->mnt_sb->s_root, &this); if (!dentry) …. d_add(dentry, inode); //Añade el inode al directorio dentry
GET_PIPE_INODE(fs/pipe.c) static struct inode* get_pipe_inode(void) {//crea el inodo struct inode *inode = new_inode(pipe_mnt->mnt_sb); if (!inode) goto fail_inode; if(!pipe_new(inode)) //asigna una pagina de memoria para contener los datos goto fail_iput; //inicializaciones PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1; inode->i_fop = &rdwr_pipe_fops;//operaciones
GET_PIPE_INODE(fs/pipe.c) inode->i_state = I_DIRTY; inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR; inode->i_uid = current->fsuid; inode->i_gid = current->fsgid; inode->i_atime = inode->i_mtime = inode->i_ctime=CURRENT_TIME; inode->i_blksize = PAGE_SIZE; return inode;
GET_PIPE_INODE(fs/pipe.c) fail_iput: iput(inode); fail_inode: return NULL; }
PIPE_NEW(fs/pipe.c) struct inode* pipe_new(struct inode* inode) { unsigned long page; page = __get_free_page(GFP_USER);//asigna 1 pagina de memoria para contener los datos if (!page) return NULL; inode->i_pipe = kmalloc(sizeof(struct pipe_inode_info), GFP_KERNEL);//Crea la estructura pipe_inode_info if (!inode->i_pipe) goto fail_page;
PIPE_NEW(fs/pipe.c) init_waitqueue_head(PIPE_WAIT(*inode)); PIPE_BASE(*inode) = (char*) page; PIPE_START(*inode) = PIPE_LEN(*inode) = 0; PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; PIPE_WAITING_READERS(*inode)= PIPE_WAITING_WRITERS(*inode) = 0; PIPE_RCOUNTER(*inode) = PIPE_WCOUNTER(*inode) = 1; return inode; fail_page: free_page(page); return NULL; }
TUBERIAS CON NOMBRECREACION • Prototipo función mkfifo : #include <sys/stat.h> int mkfifo (const char *path, mode_t mode); • Esta función se implementa con la llamada mknod. mknod (path, mode | S_IFIFO, 0); • Las tuberías con nombre permiten e/s con bloqueo o sin bloqueo. • Para no bloqueo especificar opción O_NODELAY o O_NONBLOCK al abrir con open. • Salvo lseek todas las operaciones de manipulación de un archivo son válidas.
IMPLEMENTACION :TUBERIAS CON NOMBRE • La creación de una tubería con nombre corresponde a la creación de un archivo cualquiera, y se detalla en el archivo fs/fifo.c. • Las operaciones de lectura y de escritura se encuentran en el archivo destinado a las tuberías anónimas fs/pipe.c.
IMPLEMENTACION :Creación tubería con nombre • La creación de una tubería con nombre se efectúa por la función fifo-open, definida en el archivo fuente fs/fifo.c. Esta función se llama mediante la función de inicialización de una tubería con nombre: init_fifo. • La creación de una nueva tubería con nombre depende de las opciones de la creación. Las operaciones autorizadas sobre el i-nodo se fijan en función de estas opciones. • Lectura exclusiva: connecting_fifo_fops en el caso de una lectura sin bloqueo, read_fifo_fops en caso contrario • Escritura exclusiva: write_fifo_fops • Lectura y escritura: rdwr_fifo_fops
FIFO_OPEN(fs/fifo.c) Static int fifo_open(struct inode *inode, struct file *filp) { int ret; ret = -ERESTARTSYS; lock_kernel(); if (down_interruptible(PIPE_SEM(*inode))) goto err_nolock_nocleanup; if (!inode->i_pipe) { ret = -ENOMEM; if(!pipe_new(inode)) goto err_nocleanup; }
FIFO_OPEN(fs/fifo.c) switch (filp->f_mode) { case 1: //solo lectura filp->f_op = &read_fifo_fops; PIPE_RCOUNTER(*inode)++; PIPE_READERS(*inode)++; ... case 2: //solo escritura ret = -ENXIO; ... filp->f_op = &write_fifo_fops; PIPE_WCOUNTER(*inode)++; PIPE_WRITERS(*inode)++; ...
FIFO_OPEN(fs/fifo.c) case 3: // lectura y escritura filp->f_op = &rdwr_fifo_fops; PIPE_READERS(*inode)++; PIPE_WRITERS(*inode)++; PIPE_RCOUNTER(*inode)++; PIPE_WCOUNTER(*inode)++; .... default: ... } /* Ok! */ up(PIPE_SEM(*inode)); unlock_kernel(); return 0;
Operaciones de entradas/salidas • Todas las operaciones de entradas/salidas, ya se trate de tuberías con nombre o anónimas, se encuentran en el archivo fs/pipe.c. • Las lecturas y las escrituras se efectúan en una memoria intermedia de tamaño PIPE_BUF (4KB). En función de las lecturas y escrituras de datos en la tubería, la ventana que contiene los datos de la tubería se desplaza de manera circular en el interior de la memoria intermedia.
Operaciones de entradas/salidas • pipe_read recorre la página de memoria de la tubería copiando los datos contenidos en la memoria intermedia destinada al usuario. • Durante esta operación, la tubería se bloquea. • Lectura sin bloqueo => se devuelve el error EAGAIN si la tubería está bloqueada o vacía. • Lectura con bloqueo => el proceso que llama se queda en espera mientras la tubería esté vacía.
Operaciones de entradas/salidas • pipe_write escritura en la tubería de datos pasados como parámetros en la memoria intermedia, dentro del límite de tamaño restante disponible. • Mientras dura la operación, los accesos a la tubería están bloqueados. • Esta función efectúa un bucle hasta escribir todos los datos en la tubería. • En cada pasada por el bucle, comprueba si quedan procesos lectores en la tubería. Si es así, se envía la señal SIGPIPE al proceso que llama. En caso contrario, la tubería se bloquea, los datos se copian desde la memoria intermedia proporcionada por quien llama, y la tubería se desbloquea.
Operaciones de entradas/salidas • pipe_ioctl la única operación de ioctl permitida es la designada por FIONREAD, que permite recuperar el número de bytes de datos almacenados actualmente en al tubería. Se devuelve el contenido de campo len de la escritura pipe_inode_info
Operaciones de entradas/salidas • pipe_select para operación de multiplexado asociada a la llamada select, esta función verifica ciertas condiciones de validez antes de indicar si es necesario volver a las entradas o salidas sobre la tubería • multiplexado en lectura: es necesario que la tubería no esté vacía y que al menos un proceso tenga acceso a la tubería en escritura • multiplexado en escritura: es necesario que la tubería no esté llena y que al menos un proceso tenga acceso a la tubería en lectura • excepciones: la tubería debe ser accesible al menos por un proceso en lectura y en escritura
Operaciones de entradas/salidas • Se utilizan estas funciones únicamente en el contexto de las tuberías con nombre, cuando al crear la tubería, ningún proceso ha abierto la tubería en escritura: • connect_read Sólo modifica las operaciones realizables sobre el i-nodo asignando el campo f_op a read_fifo_fops. Tras esta modificación, se lanza una llamada a la función de lectura en una tubería (pipe_read).
Operaciones de entradas/salidas • connect_select Esta función modifica el campo f_op en el caso del multiplexado en lectura.
Operaciones de entradas/salidas • Se utilizan estas funciones para cerrar descriptores: pipe_read_release decrementa nºprocesos con acceso en lectura a la tubería pipe_write_release decrementa nºprocesos con acceso en escritura a la tubería pipe_rdwr_release decrementa nºprocesos con acceso en lectura y escritura a la tubería
Operaciones de entradas/salidas • Se utilizan estas funciones para creación de un nuevo proceso que hereda la tubería: • pipe_read_open incrementa nºprocesos con acceso en lectura a la tubería pipe_read_open(struct inode *inode, struct file *filp) { down(PIPE_SEM(*inode)); PIPE_READERS(*inode)++; up(PIPE_SEM(*inode));
Operaciones de entradas/salidas • pipe_write_open incrementa nºprocesos con acceso en escritura a la tubería. pipe_write_open(struct inode *inode, struct file *filp) { down(PIPE_SEM(*inode)); PIPE_WRITERS(*inode)++; up(PIPE_SEM(*inode));
Operaciones de entradas/salidas • pipe_rdwr_open incrementa nºprocesos con acceso en lectura y escritura a la tubería. pipe_rdwr_open(struct inode *inode, struct file*filp) { down(PIPE_SEM(*inode)); if (filp->f_mode & FMODE_READ) PIPE_READERS(*inode)++; if (filp->f_mode & FMODE_WRITE) PIPE_WRITERS(*inode)++; up(PIPE_SEM(*inode)); }