Sistemas operativos: una visión aplicada Capítulo 5 Comunicación y sincronización de procesos Contenido • • • • • • • Procesos concurrentes. Problemas clásicos de comunicación y sincronización. Mecanismos de comunicación y sincronización. Paso de mensajes. Aspectos de implementación Interbloqueos. Servicios POSIX Sistemas operativos: una visión aplicada 2 © J. Carretero, F. García, P. de Miguel, F. Pérez Procesos concurrentes • Modelos – Multiprogramación en un único procesador – Multiprocesador – Multicomputador (proceso distribuido) • Razones – Compartir recursos físicos – Compartir recursos lógicos – Acelerar los cálculos – Modularidad – Comodidad Sistemas operativos: una visión aplicada 3 © J. Carretero, F. García, P. de Miguel, F. Pérez Sistema multiprogramado con un una CPU P ro c e so A P ro c e so B P ro c e so C T ie m p o Sistemas operativos: una visión aplicada 4 © J. Carretero, F. García, P. de Miguel, F. Pérez Ejecución en un sistema multiprocesador P ro c e so A CPU 1 P ro c e so B CPU 2 P ro c e so C P ro c e so D T ie m p o Sistemas operativos: una visión aplicada 5 © J. Carretero, F. García, P. de Miguel, F. Pérez Tipos de procesos concurrentes • Tipos de procesos – Independientes – Cooperantes • Interacción entre procesos – Compiten por recursos – Comparten recursos Sistemas operativos: una visión aplicada 6 © J. Carretero, F. García, P. de Miguel, F. Pérez Contenido • Procesos concurrentes. • Problemas clásicos de comunicación y sincronización. • • • • • Mecanismos de comunicación y sincronización. Paso de mensajes. Aspectos de implementación Interbloqueos. Servicios POSIX Sistemas operativos: una visión aplicada 7 © J. Carretero, F. García, P. de Miguel, F. Pérez Problemas clásicos de comunicación y sincronización • • • • El problema de la sección crítica El problema del productor-consumidor El problema de los lectores-escritores Comunicación cliente-servidor Sistemas operativos: una visión aplicada 8 © J. Carretero, F. García, P. de Miguel, F. Pérez Problema de la sección crítica • Sistema compuesto por n procesos • Cada uno tiene un fragmento de código: sección crítica • Sólo uno de los procesos en cada instante puede ejecutar en la sección crítica – Cuando un proceso está ejecutando en la sección crítica, ningún otro puede hacerlo Sistemas operativos: una visión aplicada 9 © J. Carretero, F. García, P. de Miguel, F. Pérez Ejemplo 1 P ro c e s o lig e ro p rin c ip a l ni =51 nf = 100 ni = 1 nf = 50 S 2 = 5 1 + ....+ 1 0 0 S 1 = 1 + ...+ 5 0 s u m a _ to ta l = S 1 + S 2 Sistemas operativos: una visión aplicada 10 © J. Carretero, F. García, P. de Miguel, F. Pérez Ejemplo 1 • Calcula la suma de los N primeros números utilizando procesos ligeros. int suma_total = 0; void suma_parcial(int ni, int nf) { int j = 0; int suma_parcial = 0; for (j = ni; j <= nf; j++) suma_parcial = suma_parcial + j; suma_total = suma_total + suma_parcial; pthread_exit(0); } • Si varios procesos ejecutan concurrentemente este código se puede obtener un resultado incorrecto. • Solución: secciones críticas Sistemas operativos: una visión aplicada 11 © J. Carretero, F. García, P. de Miguel, F. Pérez Ejemplo con sección crítica void suma_parcial(int ni, int nf) { int j = 0; int suma_parcial = 0; for (j = ni; j <= nf; j++) suma_parcial = suma_parcial + j; <Entrada en la sección crítica> suma_total = suma_total + suma_parcial; <Salida de la sección crítica> pthread_exit(0); } Sistemas operativos: una visión aplicada 12 © J. Carretero, F. García, P. de Miguel, F. Pérez Ejemplo 2 void ingresar(char *cuenta, int cantidad) { int saldo, fd; fd = open(cuenta, O_RDWR); read(fd, &saldo, sizeof(int)); saldo = saldo + cantidad; lseek(fd, 0, SEEK_SET); write(fd, &saldo, sizeof(int)); close(fd); return; } • Si dos procesos ejecutan concurrentemente este código se puede perder algún ingreso. • Solución: secciones críticas Sistemas operativos: una visión aplicada 13 © J. Carretero, F. García, P. de Miguel, F. Pérez Ejemplo 2 con sección crítica void ingresar(char *cuenta, int cantidad) { int saldo, fd; fd = open(cuenta, O_RDWR); <Entrada en la sección crítica> read(fd, &saldo, sizeof(int)); saldo = saldo + cantidad; lseek(fd, 0, SEEK_SET); write(fd, &saldo, sizeof(int)); <Salida de la sección crítica> close(fd); return; } Sistemas operativos: una visión aplicada 14 © J. Carretero, F. García, P. de Miguel, F. Pérez Ejemplo 3 P ro c e s a d o r 2 P ro c e s a d o r 1 L e e P ID P ID = 5 0 0 R e g is tr o o p o s ic ió n d e m e m o r ia P ID = 5 0 0 L e e P ID In c re m e n ta y a s ig n a P ID P I D = 5 0 1 E s c r ib e P ID P ID = 5 0 1 P ID = 5 0 0 In c re m e n ta y a s ig n a P I D P ID = 5 0 1 P ID = 5 0 1 Sistemas operativos: una visión aplicada 15 E s c r ib e P ID © J. Carretero, F. García, P. de Miguel, F. Pérez Solución al problema de la sección crítica • Estructura general de cualquier mecanismo utilizado para resolver el problema de la sección crítica: Entrada en la sección crítica Código de la sección crítica Salida de la sección crítica • Requisitos que debe ofrecer cualquier solución para resolver el problema de la sección crítica: – Exclusión mutua – Progreso – Espera limitada Sistemas operativos: una visión aplicada 16 © J. Carretero, F. García, P. de Miguel, F. Pérez Problema del productor-consumidor P ro c e s o C o n s u m id o r P ro c e s o P ro d u c to r F lu jo d e d a to s M e c a n is m o d e c o m u n ic a c ió n Sistemas operativos: una visión aplicada 17 © J. Carretero, F. García, P. de Miguel, F. Pérez El problema de los lectores-escritores L e c to r L e c to r E s c r ito r L e c to r E s c r ito r R e c u rs o Sistemas operativos: una visión aplicada 18 © J. Carretero, F. García, P. de Miguel, F. Pérez Comunicación cliente-servidor C o m p u ta d o r C o m p u ta d o r P e t ic ió n P ro c e s o c lie n te P ro c e s o s e r v id o r S .O . R e s p u e s ta Sistemas operativos: una visión aplicada 19 © J. Carretero, F. García, P. de Miguel, F. Pérez Contenido • Procesos concurrentes. • Problemas clásicos de comunicación y sincronización. • Mecanismos de comunicación y sincronización. • • • • Paso de mensajes. Aspectos de implementación Interbloqueos. Servicios POSIX Sistemas operativos: una visión aplicada 20 © J. Carretero, F. García, P. de Miguel, F. Pérez Mecanismos de comunicación • • • • Archivos Tuberías (pipes, FIFOS) Variables en memoria compartida Paso de mensajes Sistemas operativos: una visión aplicada 21 © J. Carretero, F. García, P. de Miguel, F. Pérez Mecanismos de Sincronización • Construcciones de los lenguajes concurrentes (procesos ligeros) • Servicios del sistema operativo: – Señales (asincronismo) – Tuberías (pipes, FIFOS) – Semáforos – Mutex y variables condicionales – Paso de mensajes • Las operaciones de sincronización deben ser atómicas Sistemas operativos: una visión aplicada 22 © J. Carretero, F. García, P. de Miguel, F. Pérez Tuberías (POSIX) • Mecanismo de comunicación y sincronización – Sin nombre: pipes – Con nombre: FIFOS • Sólo puede utilizarse entre los procesos hijos del proceso que creó el pipe int pipe(int fildes[2]); • • • Identificación: dos descriptores de archivo – Para lectura – Para escritura Flujo de datos: unidireccional Mecanismo con capacidad de almacenamiento Sistemas operativos: una visión aplicada 23 © J. Carretero, F. García, P. de Miguel, F. Pérez Comunicación unidireccional con tuberías w r ite P ro c e s o d e U s u a r io P ro c e s o d e U s u a r io re a d SO p ip e F lu jo d e d a to s Sistemas operativos: una visión aplicada 24 © J. Carretero, F. García, P. de Miguel, F. Pérez Comunicación bidireccional con tuberías w r it e P ro c e s o d e U s u a r io w r it e re a d P ro c e s o d e U s u a r io re a d SO p ip e F lu jo d e d a to s p ip e F lu jo d e d a to s Sistemas operativos: una visión aplicada 25 © J. Carretero, F. García, P. de Miguel, F. Pérez Tuberías (II) • read(fildes[0], buffer, n) – Pipe vacío se bloquea el lector – Pipe con p bytes • Si p ≥ n devuelve n • Si p < n devuelve p – Si pipe vacío y no hay escritores devuelve 0 • write(fildes[1], buffer, n) – Pipe lleno se bloquea el escritor – Si no hay lectores se recibe la señal SIGPIPE • Lecturas y escrituras atómicas (cuidado con tamaños grandes) Sistemas operativos: una visión aplicada 26 © J. Carretero, F. García, P. de Miguel, F. Pérez Secciones críticas con tuberías void main(void) { int fildes[2]; char c; /* pipe para sincronizar */ /* caracter para sincronizar */ pipe(fildes); write(fildes[1], &c, 1); /* necesario para entrar en la seccion critica la primera vez */ /* proceso hijo */ if (fork() == 0) { for(;;) { read(fildes[0], &c, 1); /* entrada seccion critica */ < Seccion critica > write(fildes[1], &c, 1); /* salida seccion critica */ } Sistemas operativos: una visión aplicada 27 © J. Carretero, F. García, P. de Miguel, F. Pérez Secciones críticas con tuberías (II) } else { /* proceso padre */ for(;;) { read(fildes[0], &c, 1); /* entrada seccion critica */ < seccion critica > write(fildes[1], &c, 1); /* salida seccion critica */ } } } Sistemas operativos: una visión aplicada 28 © J. Carretero, F. García, P. de Miguel, F. Pérez Productor-consumidor con tuberías void main(void) { int fildes[2]; /* pipe para comunicar y sincronizar */ int dato_p[4]; /* datos a producir */ int dato_c; /* dato a consumir */ pipe(fildes); if (fork() == 0) { /* proceso hijo: productor */ for(;;) { < producir dato_p, escribe 4 enteros * write(fildes[1], dato_p, 4*sizeof(int)); } Sistemas operativos: una visión aplicada 29 © J. Carretero, F. García, P. de Miguel, F. Pérez Productor-consumidor con tuberías (II) } else { /* proceso padre: consumidor */ for(;;) { read(fildes[0], &dato, sizeof(int)); /* consumir dato, lee un entero */ } } } Proceso hijo Sistemas operativos: una visión aplicada Pipe 30 Proceso padre © J. Carretero, F. García, P. de Miguel, F. Pérez Ejecución de mandatos con tuberías /* programa que ejecuta el mandato ``ls | wc'' */ void main(void) { int fd[2]; pid_t pid; ls pipe wc if (pipe(fd) < 0) { perror(``pipe''); exit(-1); } pid = fork(); switch(pid) { case -1: /* error */ perror(``fork''); exit(-1); Sistemas operativos: una visión aplicada 31 © J. Carretero, F. García, P. de Miguel, F. Pérez Ejecución de mandatos con tuberías (II) case 0: /* proceso hijo ejecuta ``ls'' */ close(fd[0]); /* cierra el pipe de lectura */ close(STDOUT_FILENO); /* cierra la salida estandar */ dup(fd[1]); close(fd[1]); execlp(``ls'',``ls'',NULL); perror(``execlp''); exit(-1); default: /* proceso padre ejecuta ``wc'' */ close(fd[1]); /* cierra el pipe de escritura */ close(STDIN_FILENO); /* cierra la entrada estandar */ dup(fd[0]); close(fd[0]); execlp(``wc'',``wc'',NULL); perror(``execlp''); } } Sistemas operativos: una visión aplicada 32 © J. Carretero, F. García, P. de Miguel, F. Pérez Ejecución de mandatos con tuberías (III) •Proceso •Proceso •Proceso •STDIN •STDOUT •fd[0] •fd[1] •STDOUT •STDIN •STDOUT •fd[0] •fd[1] •pipe •STDOUT •STDIN •pipe •exec •STDOUT •Proceso hijo •STDIN •pipe •STDOUT •wc •ls Sistemas operativos: una visión aplicada •redirección •Proceso hijo •Proceso •STDIN •pipe •Proceso hijo •Proceso •STDIN •fork() •STDIN •STDOUT •fd[0] •fd[1] •pipe(fd) •STDIN •STDOUT 33 © J. Carretero, F. García, P. de Miguel, F. Pérez Tuberías con nombre en POSIX (FIFOS) • • • • Igual que los pipes Mecanismo de comunicación y sincronización con nombre Misma máquina Servicios – int mkfifo(char *name, mode_t mode); • Crea un FIFO con nombre name – int open(char *name, int flag); • Abre un FIFO (para lectura, escritura o ambas) • Bloquea hasta que haya algún proceso en el otro extremo • Lectura y escritura mediante read() y write() – Igual semántica que los pipes • Cierre de un FIFO mediante close() • Borrado de un FIFO mediante unlink() Sistemas operativos: una visión aplicada 34 © J. Carretero, F. García, P. de Miguel, F. Pérez Semáforos • • • • Mecanismo de sincronización Misma máquina Objeto con un valor entero Dos operaciones atómicas – wait – signal Sistemas operativos: una visión aplicada 35 © J. Carretero, F. García, P. de Miguel, F. Pérez Operaciones sobre emáforos wait(s) { } s = s - 1; if (s < 0) { <Bloquear al proceso> } signal(s) { s = s + 1; if (s <= 0) <Desbloquear a un proceso bloqueado por la operacion wait> } } Sistemas operativos: una visión aplicada 36 © J. Carretero, F. García, P. de Miguel, F. Pérez Secciones críticas con semáforos wait(s); /* entrada en la seccion critica */ < seccion critica > signal(s); /* salida de la seccion critica */ • El semáforo debe tener valor inicial 1 Valor del semáforo (s) P0 P1 P2 1 0 -1 wait(s) wait(s) wait(s) -2 desbloquea -1 signal(s) desbloquea 0 signal(s) Ejecutando código de la sección crítica 1 Sistemas operativos: una visión aplicada signal(s) 37 Proceso bloqueado en el semáforo © J. Carretero, F. García, P. de Miguel, F. Pérez Semáforos POSIX • int sem_init(sem_t *sem, int shared, int val); – Inicializa un semáforo sin nombre • int sem_destroy(sem_t *sem); – Destruye un semáforo sin nombre • sem_t *sem_open(char *name, int flag, mode_t mode, int val); – Abre (crea) un semáforo con nombre. • int sem_close(sem_t *sem); – Cierra un semáforo con nombre. • int sem_unlink(char *name); – Borra un semáforo con nombre. • int sem_wait(sem_t *sem); – Realiza la operación wait sobre un semáforo. • int sem_post(sem_t *sem); – Realiza la operación signal sobre un semáforo. Sistemas operativos: una visión aplicada 38 © J. Carretero, F. García, P. de Miguel, F. Pérez Productor-consumidor con semáforos (buffer acotado y circular) Productor Consumidor Sistemas operativos: una visión aplicada 39 © J. Carretero, F. García, P. de Miguel, F. Pérez Productor-consumidor con semáforos (II) #define MAX_BUFFER #define DATOS_A_PRODUCIR sem_t elementos; sem_t huecos; int buffer[MAX_BUFFER]; void main(void) { pthread_t th1, th2; 1024 100000 /* tamanio del buffer */ /* datos a producir */ /* elementos en el buffer */ /* huecos en el buffer */ /* buffer comun */ /* identificadores de threads */ /* inicializar los semaforos */ sem_init(&elementos, 0, 0); sem_init(&huecos, 0, MAX_BUFFER); Sistemas operativos: una visión aplicada 40 © J. Carretero, F. García, P. de Miguel, F. Pérez Productor-consumidor con semáforos (III) /* crear los procesos ligeros */ pthread_create(&th1, NULL, Productor, NULL); pthread_create(&th2, NULL, Consumidor, NULL); /* esperar su finalizacion */ pthread_join(th1, NULL); pthread_join(th2, NULL); sem_destroy(&huecos); sem_destroy(&elementos); exit(0); } Sistemas operativos: una visión aplicada 41 © J. Carretero, F. García, P. de Miguel, F. Pérez Productor-consumidor con semáforos (IV) void Productor(void) /* codigo del productor */ { int pos = 0; /* posicion dentro del buffer */ int dato; /* dato a producir */ int i; for(i=0; i < DATOS_A_PRODUCIR; i++ ) { dato = i; /* producir dato */ sem_wait(&huecos); /* un hueco menos */ buffer[pos] = i; pos = (pos + 1) % MAX_BUFFER; sem_post(&elementos); /* un elemento mas */ } pthread_exit(0); } Sistemas operativos: una visión aplicada 42 © J. Carretero, F. García, P. de Miguel, F. Pérez Productor-consumidor con semáforos (V) void Consumidor(void) { int pos = 0; int dato; int i; /* codigo del Consumidor */ for(i=0; i < DATOS_A_PRODUCIR; i++ ) { sem_wait(&elementos); /* un elemento menos */ dato = buffer[pos]; pos = (pos + 1) % MAX_BUFFER; sem_post(&huecos); /* un hueco mas */ /* cosumir dato */ } pthread_exit(0); } Sistemas operativos: una visión aplicada 43 © J. Carretero, F. García, P. de Miguel, F. Pérez Lectores-escritores con semáforos int dato = 5; /* recurso */ int n_lectores = 0; /* numero de lectores */ sem_t sem_lec; /* controlar el acceso n_lectores */ sem_t mutex; /* controlar el acceso a dato */ void main(void) { pthread_t th1, th2, th3, th4; sem_init(&mutex, 0, 1); sem_init(&sem_lec, 0, 1); pthread_create(&th1, pthread_create(&th2, pthread_create(&th3, pthread_create(&th4, Sistemas operativos: una visión aplicada NULL, NULL, NULL, NULL, Lector, NULL); Escritor, NULL); Lector, NULL); Escritor, NULL); 44 © J. Carretero, F. García, P. de Miguel, F. Pérez Lectores-escritores con semáforos (II) pthread_join(th1, pthread_join(th2, pthread_join(th3, pthread_join(th4, NULL); NULL); NULL); NULL); /* cerrar todos los semaforos */ sem_destroy(&mutex); sem_destroy(&sem_lec); exit(0); } Sistemas operativos: una visión aplicada 45 © J. Carretero, F. García, P. de Miguel, F. Pérez Lectores-escritores con semáforos (III) /* codigo del lector */ void Lector(void) { /* codigo del lector */ sem_wait(&sem_lec); n_lectores = n_lectores + 1; if (n_lectores == 1) sem_wait(&mutex); sem_post(&sem_lec); printf(``%d\n'', dato); /* leer dato */ sem_wait(&sem_lec); n_lectores = n_lectores - 1; if (n_lectores == 0) sem_post(&mutex); sem_post(&sem_lec); pthread_exit(0); } Sistemas operativos: una visión aplicada 46 © J. Carretero, F. García, P. de Miguel, F. Pérez Lectores-escritores con semáforos (IV) /* código del escritor */ void Escritor(void) { sem_wait(&mutex); dato = dato + 2; sem_post(&mutex); /* codigo del escritor */ /* modificar el recurso */ pthread_exit(0); } Sistemas operativos: una visión aplicada 47 © J. Carretero, F. García, P. de Miguel, F. Pérez Memoria compartida P ro c e s o A P ro c e s o B T e x to T e x to D a to s v a r2 D a to s v a r1 2 P ila S e g m e n to d e m e m o r ia c o m p a r tid a P ila • Declaración independiente de variables Sistemas operativos: una visión aplicada 48 © J. Carretero, F. García, P. de Miguel, F. Pérez Productor-consumidor con memoria compartida • Productor: – Crea los semáforos (shm_open) – Crea la zona de memoria compartida utilizando un archivo proyectado en memoria (open) – Le asigna espacio (ftruncate) – Proyecta el archivo en su espacio de direcciones (mmap) – Utiliza la zona de memoria compartida – Desproyecta la zona de memoria compartida – Cierra y borra el archivo. • Consumidor: – – – – – Abre los semáforos (shm_open) Debe esperar a que archivo esté creado para abrirlo (open) Proyecta el archivo en su espacio de direcciones (mmap) Utiliza la zona de memoria compartida Cierra el archivo. Sistemas operativos: una visión aplicada 49 © J. Carretero, F. García, P. de Miguel, F. Pérez Código del productor #define MAX_BUFFER #define DATOS_A_PRODUCIR sem_t *elementos; sem_t *huecos; 1024 100000 /* tamaño del buffer */ /* datos a producir */ /* elementos en el buffer */ /* huecos en el buffer */ void main(int argc, char *argv[]){ int shd; int *buffer; /* buffer comun */ /* el productor crea el archivo a proyectar */ shd = open("BUFFER", O_CREAT|O_WRONLY, 0700); ftruncate(shd, MAX_BUFFER * sizeof(int)); /*proyectar el objeto de memoria compartida en el espacio de direcciones del productor*/ buffer = (int *) mmap(NULL, MAX_BUFFER * sizeof(int), PROT_WRITE, MAP_SHARED, shd, 0); Sistemas operativos: una visión aplicada 50 © J. Carretero, F. García, P. de Miguel, F. Pérez Código del productor (II) /* El productor crea los semaforos */ elementos = sem_open("ELEMENTOS", O_CREAT, 0700, 0); huecos = sem_open("HUECOS", O_CREAT, 0700, MAX_BUFFER); Productor(buffer); /* desproyectar el buffer compartido */ munmap(buffer, MAX_BUFFER * sizeof(int)); close(shd); /* cerrar el objeto de memoria compartida */ unlink("BUFFER"); /* borrar el objeto de memoria */ sem_close(elementos); sem_close(huecos); sem_unlink("ELEMENTOS"); sem_unlink("HUECOS"); } Sistemas operativos: una visión aplicada 51 © J. Carretero, F. García, P. de Miguel, F. Pérez Código del productor (III) void Productor(int *buffer) /* codigo del productor */ { int pos = 0; /* posicion dentro del buffer */ int dato; /* dato a producir */ int i; for(i=0; i < DATOS_A_PRODUCIR; i++ ) { dato = i; /* producir dato */ sem_wait(huecos); /* un hueco menos */ buffer[pos] = i; pos = (pos + 1) % MAX_BUFFER; sem_post(elementos); /* un elemento mas */ } return; } Sistemas operativos: una visión aplicada 52 © J. Carretero, F. García, P. de Miguel, F. Pérez Código del consumidor #define MAX_BUFFER #define DATOS_A_PRODUCIR sem_t *elementos; sem_t *huecos; 1024 100000 /* tamanio del buffer */ /* datos a producir */ /* elementos en el buffer */ /* huecos en el buffer */ void main(int argc, char *argv[]){ int shd; int *buffer; /* buffer comun */ /* el consumidor abre el archivo a proyectar */ shd = open("BUFFER", O_RDONLY); /*proyectar el objeto de memoria compartida en el espacio de direcciones del productor*/ buffer = (int *) mmap(NULL, MAX_BUFFER * sizeof(int), PROT_READ, MAP_SHARED, shd, 0); Sistemas operativos: una visión aplicada 53 © J. Carretero, F. García, P. de Miguel, F. Pérez Código del consumidor (II) /* El consumidor abre los semaforos */ elementos = sem_open("ELEMENTOS", 0); huecos = sem_open("HUECOS", 0); Consumidor(buffer); /* desproyectar el buffer compartido */ munmap(buffer, MAX_BUFFER * sizeof(int)); close(shd); /* cerrar el objeto de memoria compartida */ /* cerrar los semaforos */ sem_close(elementos); sem_close(huecos); } Sistemas operativos: una visión aplicada 54 © J. Carretero, F. García, P. de Miguel, F. Pérez Código del consumidor (III) void Consumidor(char *buffer) { int pos = 0; int i, dato; /* codigo del Consumidor */ for(i=0; i < DATOS_A_PRODUCIR; i++ ) { sem_wait(elementos); /* un elemento menos */ dato = buffer[pos]; pos = (pos + 1) % MAX_BUFFER; sem_post(huecos); /* un hueco mas */ printf("Consume %d \n", dato); /* cosumir dato */ } return; } Sistemas operativos: una visión aplicada 55 © J. Carretero, F. García, P. de Miguel, F. Pérez Mutex y variables condicionales • Un mutex es un mecanismo de sincronización indicado para procesos ligeros. • Es un semáforo binario con dos operaciones atómicas: – lock(m) Intenta bloquear el mutex, si el mutex ya está bloqueado el proceso se suspende. – unlock(m) Desbloquea el mutex, si existen procesos bloqueados en el mutex se desbloquea a uno. Sistemas operativos: una visión aplicada 56 © J. Carretero, F. García, P. de Miguel, F. Pérez Secciones críticas con mutex lock(m); /* entrada en la seccion critica */ < seccion critica > unlock(s); /* salida de la seccion critica */ • La operación unlock debe realizarla el proceso ligero que ejecutó lock P ro c e s o lig e r o A P ro c e s o lig e r o B lo c k m u te x lo c k m u te x S e c c ió n c r ític a o b tie n e m u te x u n lo c k m u te x P r o c e s o lig e r o e je c u t a n d o P r o c e s o lig e r o b lo q u e a d o P u n t o d e s in c r o n iz a c ió n Sistemas operativos: una visión aplicada 57 © J. Carretero, F. García, P. de Miguel, F. Pérez Variables condicionales • Variables de sincronización asociadas a un mutex • Conveniente ejecutarlas entre lock y unlock • Dos operaciones atómicas: – wait Bloquea al proceso ligero que la ejecuta y le expulsa del mutex – signal Desbloquea a uno o varios procesos suspendidos en la variable condicional. El proceso que se despierta compite de nuevo por el mutex Sistemas operativos: una visión aplicada 58 © J. Carretero, F. García, P. de Miguel, F. Pérez Variables condicionales (II) P ro c e s o lig e r o A lo c k w a it P ro c e s o lig e r o B lo c k A d q u ie r e e l m u te x u n lo c k m u t e x s ig n a l S e c o m p it e p o r e l m u t e x A d q u ie r e e l m u te x u n lo c k P r o c e s o lig e r o b lo q u e a d o e s p e r a n d o u n lo c k P r o c e s o lig e r o b lo q u e a d o e s p e r a n d o s ig n a l Sistemas operativos: una visión aplicada 59 © J. Carretero, F. García, P. de Miguel, F. Pérez Uso de mutex y variables condicionales • Proceso ligero A lock(mutex); /* acceso al recurso */ comprobar las estructuras de datos; while (recurso ocupado) wait(condition, mutex); marcar el recurso como ocupado; unlock(mutex); • Proceso ligero B lock(mutex); /* acceso al recurso */ marcar el recurso como libre; signal(condition, mutex); unlock(mutex); • Importante utilizar while Sistemas operativos: una visión aplicada 60 © J. Carretero, F. García, P. de Miguel, F. Pérez Servicios POSIX int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t * attr); – Inicializa un mutex. int pthread_mutex_destroy(pthread_mutex_t *mutex) ; – Destruye un mutex. int pthread_mutex_lock(pthread_mutex_t *mutex); – Intenta obtener el mutex. Bloquea al proceso ligero si el mutex se encuentra adquirido por otro proceso ligero. int pthread_mutex_unlock(pthread_mutex_t *mutex); – Desbloquea el mutex. int pthread_cond_init(pthread_cond_t*cond, pthread_condattr_t*attr); – Inicializa una variable condicional. Sistemas operativos: una visión aplicada 61 © J. Carretero, F. García, P. de Miguel, F. Pérez Servicios POSIX (II) int pthread_cond_destroy(pthread_cond_t *cond); – Destruye un variable condicional. int pthread_cond_signal(pthread_cond_t *cond); – Se reactivan uno o más de los procesos ligeros que están suspendidos en la variable condicional cond. – No tiene efecto si no hay ningún proceso ligero esperando (diferente a los semáforos). int pthread_cond_broadcast(pthread_cond_t *cond); – Todos los threads suspendidos en la variable condicional cond se reactivan. – No tiene efecto si no hay ningún proceso ligero esperando. int pthread_cond_wait(pthread_cond_t*cond, pthread_mutex_t*mutex); – Suspende al proceso ligero hasta que otro proceso señaliza la variable condicional cond. – Automáticamente se libera el mutex. Cuando se despierta el proceso ligero vuelve a competir por el mutex. Sistemas operativos: una visión aplicada 62 © J. Carretero, F. García, P. de Miguel, F. Pérez Productor-consumidor con mutex #define MAX_BUFFER #define DATOS_A_PRODUCIR 1024 100000 /* tamanio del buffer */ /* datos a producir */ pthread_mutex_t mutex; buffer compartido */ pthread_cond_t no_lleno; pthread_cond_t no_vacio; int n_elementos; /* mutex para controlar el acceso al int buffer[MAX_BUFFER]; /* buffer comun */ /* controla el llenado del buffer */ /* controla el vaciado del buffer */ /* numero de elementos en el buffer */ main(int argc, char *argv[]){ pthread_t th1, th2; pthread_mutex_init(&mutex, NULL); pthread_cond_init(&no_lleno, NULL); pthread_cond_init(&no_vacio, NULL); Sistemas operativos: una visión aplicada 63 © J. Carretero, F. García, P. de Miguel, F. Pérez Productor-consumidor con mutex (II) pthread_create(&th1, NULL, Productor, NULL); pthread_create(&th2, NULL, Consumidor, NULL); pthread_join(th1, NULL); pthread_join(th2, NULL); pthread_mutex_destroy(&mutex); pthread_cond_destroy(&no_lleno); pthread_cond_destroy(&no_vacio); exit(0); } Sistemas operativos: una visión aplicada 64 © J. Carretero, F. García, P. de Miguel, F. Pérez Productor-consumidor con mutex (III) void Productor(void) { int dato, i ,pos = 0; /* codigo del productor */ for(i=0; i < DATOS_A_PRODUCIR; i++ ) { dato = i; /* producir dato */ pthread_mutex_lock(&mutex); /* acceder al buffer */ while (n_elementos == MAX_BUFFER) /* si buffer lleno */ pthread_cond_wait(&no_lleno, &mutex); /* se bloquea */ buffer[pos] = i; pos = (pos + 1) % MAX_BUFFER; n_elementos ++; pthread_cond_signal(&no_vacio); /* buffer no vacio */ pthread_mutex_unlock(&mutex); } pthread_exit(0); } Sistemas operativos: una visión aplicada 65 © J. Carretero, F. García, P. de Miguel, F. Pérez Productor-consumidor con mutex (IV) void Consumidor(void) { /* codigo del sonsumidor */ int dato, i ,pos = 0; for(i=0; i < DATOS_A_PRODUCIR; i++ ) { pthread_mutex_lock(&mutex); /* acceder al buffer */ while (n_elementos == 0) /* si buffer vacio */ pthread_cond_wait(&no_vacio, &mutex); /* se bloquea */ dato = buffer[pos]; pos = (pos + 1) % MAX_BUFFER; n_elementos --; pthread_cond_signal(&no_lleno); /* buffer no lleno */ pthread_mutex_unlock(&mutex); printf("Consume %d \n", dato); /* consume dato */ } pthread_exit(0); } Sistemas operativos: una visión aplicada 66 © J. Carretero, F. García, P. de Miguel, F. Pérez Lectores-escritores con mutex int dato = 5; int n_lectores = 0; pthread_mutex_t mutex; pthread_mutex_t mutex_lectores; */ /* /* /* /* recurso */ numero de lectores */ controlar el acceso a dato */ controla la variable n_lectores main(int argc, char *argv[]) { pthread_t th1, th2, th3, th4; pthread_mutex_init(&mutex, NULL); pthread_cond_init(&no_lectores, NULL); pthread_create(&th1, pthread_create(&th2, pthread_create(&th3, pthread_create(&th4, Sistemas operativos: una visión aplicada NULL, NULL, NULL, NULL, Lector, NULL); Escritor, NULL); Lector, NULL); Escritor, NULL); 67 © J. Carretero, F. García, P. de Miguel, F. Pérez Lectores-escritores con mutex (II) pthread_join(th1, NULL); pthread_join(th2, NULL); pthread_join(th3, NULL); pthread_join(th4, NULL); pthread_mutex_destroy(&mutex); pthread_cond_destroy(&no_lectores); exit(0); } void Escritor(void) { /* codigo del escritor */ pthread_mutex_lock(&mutex); dato = dato + 2; /* modificar el recurso */ pthread_mutex_unlock(&mutex); pthread_exit(0); } Sistemas operativos: una visión aplicada 68 © J. Carretero, F. García, P. de Miguel, F. Pérez Lectores-escritores con mutex (III) void Lector(void) { /* codigo del lector */ pthread_mutex_lock(&mutex_lectores); n_lectores++; if (n_lectores == 1) pthread_mutex_lock(&mutex); pthread_mutex_unlock(&mutex_lectores); printf("%d\n", dato); /* leer dato */ pthread_mutex_lock(&mutex_lectores); n_lectores--; if (n_lectores == 0) pthread_mutex_unlock(&mutex); pthread_mutex_unlock(&mutex_lectores); pthread_exit(0); } Sistemas operativos: una visión aplicada 69 © J. Carretero, F. García, P. de Miguel, F. Pérez Contenido • Procesos concurrentes. • Problemas clásicos de comunicación y sincronización. • Mecanismos de comunicación y sincronización. • Paso de mensajes. • Aspectos de implementación • Interbloqueos. • Servicios POSIX Sistemas operativos: una visión aplicada 70 © J. Carretero, F. García, P. de Miguel, F. Pérez Paso de mensajes • Permite resolver: – Exclusión mutua – Sincronizar entre un proceso que recibe un mensaje y otro que lo envía – Comunicación de datos entre espacios de memoria diferentes (mismo computador, diferentes computadores) • Primitivas básicas: – send(destino, mensaje) envía un mensaje al proceso destino – receive(destino, mensaje) recibe un mensaje del proceso destino • Múltiples soluciones • Aspectos de diseño – Tamaño del mensaje – Flujo de datos (unidireccional, bidireccional) – Nombrado • Directo • Indirecto (puertos, colas) – Sincronización (síncrono, asíncrono) – Almacenamiento Sistemas operativos: una visión aplicada 71 © J. Carretero, F. García, P. de Miguel, F. Pérez Uso de colas y puertos P r o c e s o c lie n te P r o c e s o c lie n te send P r o c e s o c lie n t e r e c e iv e P r o c e s o c lie n te send P u e rto m e n s a je m e n s a je C o la d e m e n s a je s C o m u n ic a c ió n c o n c o la s d e m e n s a je s Sistemas operativos: una visión aplicada C o m u n ic a c ió n c o n p u e r to s 72 © J. Carretero, F. García, P. de Miguel, F. Pérez Colas de mensajes POSIX mqd_t mq_open(char *name, int flag, mode_t mode, mq_attr *attr); – Crea una cola de mensajes con nombre y atributos attr: • Número máximo de mensajes. • Tamaño máximo del mensaje. • Bloqueante, No bloqueante. int mq_close (mqd_t mqdes); – Cierra una cola de mensajes. int mq_unlink(char *name); – Borra una cola de mensajes. Sistemas operativos: una visión aplicada 73 © J. Carretero, F. García, P. de Miguel, F. Pérez Colas de mensajes POSIX (II) int mq_send(mqd_t mqdes, char *msg, size_t len, int prio); – Envía el mensaje msg de longitud len a la cola de mensajes mqdes con prioridad prio; – Si la cola está llena el envío puede ser bloqueante o no. int mq_receive(mqd_t mqdes, char *msg, size_t len, int prio); – Recibe un mensaje msg de longitud len de la cola de mensajes mqdes con prioridad prio; – Recepción bloqueante o no. Sistemas operativos: una visión aplicada 74 © J. Carretero, F. García, P. de Miguel, F. Pérez Secciones críticas con colas de mensajes void main(void) { mqd_t mutex; /* cola de mensajes para sincronizar el acceso a la seccion critica */ struct mq_attr attr; /* atributos de la cola de mensajes */ char c; /* caracter para sincronizar */ attr.mq_maxmsg = 1; attr.mq_msgsize = 1; /* numero maximo de mensajes */ /* tamanio del mensaje */ mutex = mq_open(``MUTEX'', O_CREAT|O_RDWR, 0777, &attr); mq_send(mutex, &c, 1, 0); seccion /* necesario para entrar en la critica la primera vez */ Sistemas operativos: una visión aplicada 75 © J. Carretero, F. García, P. de Miguel, F. Pérez Secciones críticas con colas de mensajes (II) if (fork() == 0) { /* proceso hijo */ for(;;) { mq_receive(mutex, &c, 1, 0); /* entrada seccion critica */ /* seccion critica */ mq_send(mutex, &c, 1, 0); /* salida seccion critica */ } else { /* proceso padre */ for(;;) { mq_receive(mutex, &c, 1, 0); /* entrada seccion critica */ /* seccion critica */ mq_send(mutex, &c, 1, 0); /* salida seccion critica */ } } } Sistemas operativos: una visión aplicada 76 © J. Carretero, F. García, P. de Miguel, F. Pérez Productor-consumidor con colas de mensajes #define MAX_BUFFER 1024 /* tamanio del buffer */ #define DATOS_A_PRODUCIR 100000 /* datos a producir */ mqd_t almacen; /* cola de mensaje donde dejar los datos producidos y recoger los datos a consumir */ void main(void) { struct mq_attr attr; attr.mq_maxmsg = MAX_BUFFER; attr.mq_msgsize = sizeof(int); almacen = mq_open("ALMACEN", O_CREAT|O_RDWR, 0777, &attr); if (almacen == -1) { perror("mq_open"); exit(0); } Sistemas operativos: una visión aplicada 77 © J. Carretero, F. García, P. de Miguel, F. Pérez Productor-consumidor con colas de mensajes (II) if (fork() == 0) /* proceso hijo */ Productor(); else /* proceso padre */ Consumidor(); exit(0); } void Productor(void) /* codigo del productor */ { int dato, i; for(i=0; i < DATOS_A_PRODUCIR; i++ ) { dato = i; /* producir dato */ printf("Produce %d \n", dato); mq_send(almacen, &dato, sizeof(int), 0); } return; } Sistemas operativos: una visión aplicada 78 © J. Carretero, F. García, P. de Miguel, F. Pérez Productor-consumidor con colas de mensajes (III) /* codigo del consumidor */ void Consumidor(void) { int dato; int i; for(i=0; i < DATOS_A_PRODUCIR; i++ ) { mq_receive(almacen, &dato, sizeof(int), 0); /* cosumir dato */ printf("Consume %d \n", dato); } return; } Sistemas operativos: una visión aplicada 79 © J. Carretero, F. García, P. de Miguel, F. Pérez Servidor multithread con colas de mensajes P r o c e s o c lie n te C o la d e l c lie n te P r o c e s o c lie n te C o la d e l c lie n te p e tic ió n p e tic ió n P r o c e s o s e r v id o r re s p u e s ta C o la d e l s e r v id o r re s p u e s ta C r e a c ió n d e l th re a d C r e a c ió n d e l th re a d T h re a d q u e s ir v e la p e t ic ió n Sistemas operativos: una visión aplicada T h re a d q u e s ir v e la p e tic ió n 80 © J. Carretero, F. García, P. de Miguel, F. Pérez Servidor multithread con colas de mensajes (II) /* estructura de un mensaje */ struct mensaje { char buffer[1024]; /* datos a enviar */ char cliente[256]; /* cola del cliente */ }; /* mutex y variables condicionales para proteger la copia del mensaje */ pthread_mutex_t mutex_mensaje; int mensaje_no_copiado = TRUE; pthread_cond_t cond; void main(void){ mqd_t q_servidor; struct mensaje mess; struct mq_attr q_attr; pthread_attr_t t_attr; Sistemas operativos: una visión aplicada /* /* /* /* cola del servidor */ mensaje a recibir */ atributos de la cola */ atributos de los threads */ 81 © J. Carretero, F. García, P. de Miguel, F. Pérez Servidor multithread con colas de mensajes (II) attr.mq_maxmsg = 20; attr.mq_msgsize = sizeof(struct mensaje)); q_servidor = mq_open("SERVIDOR", O_CREAT|O_RDONLY, 0700, &attr); pthread_mutex_init(&mutex_mensaje, NULL); pthread_cond_init(&cond, NULL); pthread_attr_init(&attr); pthread_attr_setscope(&attr,PTHREAD_SCOPE_SYSTEM); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); Sistemas operativos: una visión aplicada 82 © J. Carretero, F. García, P. de Miguel, F. Pérez Servidor multithread con colas de mensajes (III) while (TRUE) { mq_receive(q_servidor, &mess, sizeof(struct mensaje), 0); pthread_create(&thid, &attr, tratar_mensaje, &mess); /* se espera a que el thread copie el mensaje */ pthread_mutex_lock(&mutex_mensaje); while (mensaje_no_copiado) pthread_cond_wait(&cond, &mutex_mensaje); mensaje_no_copiado = TRUE; pthread_mutex_unlock(&mutex_mensaje); } } Sistemas operativos: una visión aplicada 83 © J. Carretero, F. García, P. de Miguel, F. Pérez Servidor multithread con colas de mensajes (IV) void tratar_mensaje(struct mensaje *mes) { struct mensaje mensaje_local; struct mqd_t q_cliente; /* cola del cliente */ struct mensaje respueta; /* mensaje de respuesta al cliente */ /* el thread copia el mensaje */ pthread_mutex_lock(&mutex_mensaje); memcpy((char *) &mensaje_local, (char *)&mes, sizeof(struct mensaje)); /* ya se puede despertar al servidor*/ pthread_mutex_lock(&mutex_mensaje); mensaje_no_copiado = FALSE; pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex_mensaje); Sistemas operativos: una visión aplicada 84 © J. Carretero, F. García, P. de Miguel, F. Pérez Servidor multithread con colas de mensajes (V) /* ejecutar la petición del cliente */ /* y preparar respuesta */ /* responder al cliente a su cola */ q_cliente = mq_open(mensaje_local.nombre, O_WRONLY); mqsend(q_cliente, (char *) &respueta, sizeof(respuesta), 0); mq_clise(q_cliente); pthread_exit(0); } Sistemas operativos: una visión aplicada 85 © J. Carretero, F. García, P. de Miguel, F. Pérez Ejemplo de código cliente /* estructura de un mensaje */ struct mensaje { char buffer[1024]; /* datos a enviar */ char cliente[256]; /* cola del cliente */ }; void main(void) { mqd_t q_servidor; mqd_t q_cliente; struct mq_attr q_attr; struct mensaje peticion; struct mensaje respuesta; /* /* /* /* /* cola del servidor */ cola del cliente */ atributos de la cola */ peticion al servidor */ respuesta del servidor */ attr.mq_maxmsg = 1; attr.mq_msgsize = sizeof(struct mensaje)); Sistemas operativos: una visión aplicada 86 © J. Carretero, F. García, P. de Miguel, F. Pérez Ejemplo de código cliente (II) q_cliente = mq_open("CLIENTE", O_CREAT|O_RDONLY, 0700, 0); q_servidor = mq_open("SERVIDOR", O_WRONLY); /* preparar peticion */ mq_send(q_servidor, &petcicion, sizeof(struct mensaje), 0); /* esperar respuesta */ mq_receive(q_cliente, &respuesta, sizeof(struct mensaje), 0); mq_close(q_servidor); mq_close(q_cliente); mq_unlink("CLIENTE"); exit(0); } Sistemas operativos: una visión aplicada 87 © J. Carretero, F. García, P. de Miguel, F. Pérez Contenido • • • • Procesos concurrentes. Problemas clásicos de comunicación y sincronización. Mecanismos de comunicación y sincronización. Paso de mensajes. • Aspectos de implementación • Interbloqueos. • Servicios POSIX Sistemas operativos: una visión aplicada 88 © J. Carretero, F. García, P. de Miguel, F. Pérez Implementación de mecanismos de sincronización • Espera activa: wait(s) { s = s – 1; while (s < 0) ; signal(s) { s = s + 1; • Espera pasiva wait(s) { s = s – 1; if (s < 0) Bloquear al proceso; signal(s) { s = s + 1; if (s <= 0) Desbloquear a un proceso bloqueado en la operación wait; } Sistemas operativos: una visión aplicada 89 © J. Carretero, F. García, P. de Miguel, F. Pérez Implementación de la espera pasiva • Operaciones para bloquear a un proceso en un semáforo T a b la d e p r o c e s o s BCP1 E s ta d o P ID BCP2 B lo q . 7 0 C o la a s o c ia d a a l s e m á fo ro BCP3 BC P4 BCP5 BCP6 BCP7 BCP8 6 1 E je c . 11 5 0 BC P9 B C P 1 0 B C P 11 B C P 1 2 8 9 7 (a ) T a b la d e p r o c e s o s BCP1 BCP2 0 B lo q . 7 E s ta d o P ID C o la a s o c ia d a a l s e m á fo ro 7 BCP3 BC P4 6 BCP5 BCP6 1 B lo q . 11 BCP7 BCP8 BC P9 B C P 1 0 B C P 11 B C P 1 2 B lo q . 5 0 8 9 11 (b ) Sistemas operativos: una visión aplicada 90 © J. Carretero, F. García, P. de Miguel, F. Pérez Implementación de la espera pasiva • Operaciones para desbloquear a un proceso bloqueado en un semáforo T a b la d e p r o c e s o s BC P1 BC P2 0 B lo q . 7 E s ta d o P ID C o la a s o c ia d a a l s e m á fo ro 7 BC P3 BC P4 BC P5 BCP6 BC P7 BC P8 6 1 B lo q . 11 5 0 BC P9 B C P 1 0 B C P 11 B C P 12 B lo q . 8 9 11 (a ) T a b la d e p r o c e s o s BC P1 BC P2 0 L is to 7 E s ta d o P ID C o la a s o c ia d a a l s e m á fo ro BC P3 BC P4 BC P5 BCP6 BC P7 BC P8 6 1 B lo q . 11 5 0 BC P9 B C P 1 0 B C P 11 B C P 12 B lo q . 8 9 11 (b ) Sistemas operativos: una visión aplicada 91 © J. Carretero, F. García, P. de Miguel, F. Pérez Instrucciones hardware especiales • Operación atómicas int test-and-set(int *valor) { int temp; } temp = *valor; *valor = 1; return temp; /* true */ void swap (int *a, int *b) { int temp; } temp = *a; *a = *b; *b = temp; return; Sistemas operativos: una visión aplicada 92 © J. Carretero, F. García, P. de Miguel, F. Pérez Sección crítica con test-and-set • Los procesos comparten la variable lock (con valor inicial a false) while (test-and-set(&lock)) ; <Código de la sección crítica> lock = false; Sistemas operativos: una visión aplicada 93 © J. Carretero, F. García, P. de Miguel, F. Pérez Sección crítica con swap • Los procesos comparten la variable lock con valor inicial false. • Cada proceso utiliza una variable local llave. llave = true; do swap(lock, llave); while (llave != false); <Código de la sección crítica> lock = false; Sistemas operativos: una visión aplicada 94 © J. Carretero, F. García, P. de Miguel, F. Pérez Implementación de un semáforo con test-andset wait(s){ while (test-and-set(&valor_s)) ; s = s - 1; if (s < 0){ valor_s = false; Bloquear al proceso; } else valor_s = false; } Sistemas operativos: una visión aplicada signal(s){ while (test-and-set(&valor_s)) ; s = s + 1; if ( s <= 0){ Desbloquear a un proceso bloqueado en la operación wait; } valor_s = false; } 95 © J. Carretero, F. García, P. de Miguel, F. Pérez Contenido • • • • • Procesos concurrentes. Problemas clásicos de comunicación y sincronización. Mecanismos de comunicación y sincronización. Paso de mensajes. Aspectos de implementación • Interbloqueos. • Servicios POSIX Sistemas operativos: una visión aplicada 96 © J. Carretero, F. García, P. de Miguel, F. Pérez Interbloqueos • Bloqueo permanente de un conjunto de procesos que compiten por los recursos del sistema o se comunican entre sí. • Ejemplo: Si P y Q con semáforos con valor inicial 1 P1 wait(P) wait(Q) ... signal(P) signal(Q) Sistemas operativos: una visión aplicada P2 wait(Q) wait(P) ... signal(Q) siangl(P) 97 © J. Carretero, F. García, P. de Miguel, F. Pérez Ejemplo de interbloqueo P A B Q Sistemas operativos: una visión aplicada 98 © J. Carretero, F. García, P. de Miguel, F. Pérez Interbloqueos II • Ejemplo: Si C1 y C2 son dos colas de mensajes: P1 receive(P2, M) ... send(P2, M) • P2 receive(P1, N) ... send(P1, N) Condiciones del interbloqueo: – Exclusión mutua – Retención y espera – No apropiación – Espera circular Sistemas operativos: una visión aplicada 99 © J. Carretero, F. García, P. de Miguel, F. Pérez Contenido • • • • • • Procesos concurrentes. Problemas clásicos de comunicación y sincronización. Mecanismos de comunicación y sincronización. Paso de mensajes. Aspectos de implementación Interbloqueos. • Servicios POSIX Sistemas operativos: una visión aplicada 100 © J. Carretero, F. García, P. de Miguel, F. Pérez Tuberías • Crear una tubería sin nombre – int pipe(int fildes[2]); • Crear una tuberías con nombre – int mkfifo(char *name, mode_t mode); • Abrir una tubería con nombre – int open(char *fifo, int flag); • Cerrar una tubería – int close(int fd); • Borrar una tubería con nombre – int unlink(char *fifo); • Leer de una tubería – int read(fildes[0], buffer, n); • Escribir en una tubería – int write(fildes[1], buffer, n); Sistemas operativos: una visión aplicada 101 © J. Carretero, F. García, P. de Miguel, F. Pérez Semáforos • int sem_init(sem_t *sem, int shared, int val); – Inicializa un semáforo sin nombre • int sem_destroy(sem_t *sem); – Destruye un semáforo sin nombre • sem_t *sem_open(char *name, int flag, mode_t mode, int val); – Abre (crea) un semáforo con nombre. • int sem_close(sem_t *sem); – Cierra un semáforo con nombre. • int sem_unlink(char *name); – Borra un semáforo con nombre. • int sem_wait(sem_t *sem); – Realiza la operación wait sobre un semáforo. • int sem_post(sem_t *sem); – Realiza la operación signal sobre un semáforo. Sistemas operativos: una visión aplicada 102 © J. Carretero, F. García, P. de Miguel, F. Pérez Mutex • int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t * attr); – Inicializa un mutex. • int pthread_mutex_destroy(pthread_mutex_t *mutex) – Destruye un mutex. • int pthread_mutex_lock(pthread_mutex_t *mutex); – Intenta obtener el mutex. Bloquea al proceso ligero si el mutex se encuentra adquirido por otro proceso ligero. • int pthread_mutex_unlock(pthread_mutex_t *mutex); – Desbloquea el mutex. Sistemas operativos: una visión aplicada 103 ; © J. Carretero, F. García, P. de Miguel, F. Pérez Variables condicionales • int pthread_cond_init(pthread_cond_t*cond, pthread_condattr_t*attr); – Inicializa una variable condicional. • • • • int pthread_cond_destroy(pthread_cond_t *cond); – Destruye un variable condicional. int pthread_cond_signal(pthread_cond_t *cond); – Se reactivan uno o más de los procesos ligeros que están suspendidos en la variable condicional cond. – No tiene efecto si no hay ningún proceso ligero esperando (diferente a los semáforos). int pthread_cond_broadcast(pthread_cond_t *cond); – Todos los threads suspendidos en la variable condicional cond se reactivan. – No tiene efecto si no hay ningún proceso ligero esperando. int pthread_cond_wait(pthread_cond_t*cond, pthread_mutex_t*mutex); – Suspende al proceso ligero hasta que otro proceso señaliza la variable condicional cond. – Automáticamente se libera el mutex. Cuando se despierta el proceso ligero vuelve a competir por el mutex. Sistemas operativos: una visión aplicada 104 © J. Carretero, F. García, P. de Miguel, F. Pérez Colas de mensajes • mqd_t mq_open(char *name, int flag, mode_t mode, mq_attr *attr); – Crea una cola de mensajes con nombre y atributos attr: • Número máximo de mensajes. • Tamaño máximo del mensaje. • Bloqueante, No bloqueante. • int mq_close (mqd_t mqdes); – Cierra una cola de mensajes. • int mq_unlink(char *name); – Borra una cola de mensajes. Sistemas operativos: una visión aplicada 105 © J. Carretero, F. García, P. de Miguel, F. Pérez Colas de mensajes (II) • int mq_send(mqd_t mqdes, char *msg, size_t len, int prio); – Envía el mensaje msg de longitud len a la cola de mensajes mqdes con prioridad prio; – Si la cola está llena el envío puede ser bloqueante o no. • int mq_receive(mqd_t mqdes, char *msg, size_t len, int prio); – Recibe un mensaje msg de longitud len de la cola de mensajes mqdes con prioridad prio; – Recepción bloqueante o no. Sistemas operativos: una visión aplicada 106 © J. Carretero, F. García, P. de Miguel, F. Pérez