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 Servicios Win32 Sistemas operativos: una visión aplicada 1 © J. Carretero, F. García, P. de Miguel, F. Pérez 1 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 2 © J. Carretero, F. García, P. de Miguel, F. Pérez Sistema multiprogramado con un una CPU Proceso A Proceso B Proceso C Tiempo Sistemas operativos: una visión aplicada 3 © J. Carretero, F. García, P. de Miguel, F. Pérez 2 Ejecución en un sistema multiprocesador Proceso A CPU 1 Proceso B CPU 2 Proceso C Proceso D Tiempo Sistemas operativos: una visión aplicada 4 © 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 5 © J. Carretero, F. García, P. de Miguel, F. Pérez 3 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 6 © 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 7 © J. Carretero, F. García, P. de Miguel, F. Pérez 4 Ejemplo 1 Proceso ligero principal ni =51 nf = 100 ni = 1 nf = 50 S2 = 51 +....+ 100 S1 = 1 +...+ 50 suma_total = S1 + S2 Sistemas operativos: una visión aplicada 8 © 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 9 © J. Carretero, F. García, P. de Miguel, F. Pérez 5 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 10 © 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 11 © J. Carretero, F. García, P. de Miguel, F. Pérez 6 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 12 © J. Carretero, F. García, P. de Miguel, F. Pérez Ejemplo 3 Procesador 2 Procesador 1 Lee PID Registro o posición de memoria PID = 500 PID = 500 Lee PID Incrementa y asigna PID PID = 501 Escribe PID PID = 501 PID = 500 Incrementa y asigna PID PID = 501 PID = 501 Sistemas operativos: una visión aplicada 13 Escribe PID © J. Carretero, F. García, P. de Miguel, F. Pérez 7 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 14 © J. Carretero, F. García, P. de Miguel, F. Pérez Problema del productor-consumidor Proceso Consumidor Proceso Productor Flujo de datos Mecanismo de comunicación Sistemas operativos: una visión aplicada 15 © J. Carretero, F. García, P. de Miguel, F. Pérez 8 El problema de los lectores-escritores Lector Lector Escritor Lector Escritor Recurso Sistemas operativos: una visión aplicada 16 © J. Carretero, F. García, P. de Miguel, F. Pérez Comunicación cliente-servidor Computador Computador Petición Proceso cliente Proceso servidor S.O. Respuesta Sistemas operativos: una visión aplicada 17 © J. Carretero, F. García, P. de Miguel, F. Pérez 9 Mecanismos de comunicación • • • • Archivos Tuberías (pipes, FIFOS) Variables en memoria compartida Paso de mensajes Sistemas operativos: una visión aplicada 18 © 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 19 © J. Carretero, F. García, P. de Miguel, F. Pérez 10 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 20 © J. Carretero, F. García, P. de Miguel, F. Pérez Comunicación unidireccional con tuberías write Proceso de Usuario Proceso de Usuario read SO pipe Flujo de datos Sistemas operativos: una visión aplicada 21 © J. Carretero, F. García, P. de Miguel, F. Pérez 11 Comunicación bidireccional con tuberías write Proceso de Usuario write read Proceso de Usuario read SO pipe Flujo de datos pipe Flujo de datos Sistemas operativos: una visión aplicada 22 © 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 23 © J. Carretero, F. García, P. de Miguel, F. Pérez 12 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 24 © 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 25 © J. Carretero, F. García, P. de Miguel, F. Pérez 13 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 26 © 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 27 Proceso padre © J. Carretero, F. García, P. de Miguel, F. Pérez 14 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 28 © 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 29 © J. Carretero, F. García, P. de Miguel, F. Pérez 15 Ejecución de mandatos con tuberías (III) •Proceso •Proceso •STDIN •STDOUT •Proceso •STDIN •STDOUT •fd[0] •fd[1] •fork() •STDIN •STDOUT •fd[0] •fd[1] •pipe(fd) •pipe •Proceso hijo •STDIN •STDOUT •fd[0] •fd[1] •pipe •Proceso •Proceso hijo •STDIN •STDIN •pipe •exec •STDOUT •STDOUT •Proceso •Proceso hijo •STDIN •STDIN •pipe •STDOUT •STDOUT •ls •wc Sistemas operativos: una visión aplicada •redirección 30 © 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 31 © J. Carretero, F. García, P. de Miguel, F. Pérez 16 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 32 © J. Carretero, F. García, P. de Miguel, F. Pérez Operaciones sobre semá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 33 © J. Carretero, F. García, P. de Miguel, F. Pérez 17 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 Proceso bloqueado en el semáforo 1 Sistemas operativos: una visión aplicada signal(s) 34 © 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 35 © J. Carretero, F. García, P. de Miguel, F. Pérez 18 Productor-consumidor con semáforos (buffer acotado y circular) Productor Consumidor Sistemas operativos: una visión aplicada 36 © 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 37 © J. Carretero, F. García, P. de Miguel, F. Pérez 19 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 38 © 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 39 © J. Carretero, F. García, P. de Miguel, F. Pérez 20 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 */ /* consumir dato */ } pthread_exit(0); } Sistemas operativos: una visión aplicada 40 © 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); 41 © J. Carretero, F. García, P. de Miguel, F. Pérez 21 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 42 © 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 43 © J. Carretero, F. García, P. de Miguel, F. Pérez 22 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 44 © J. Carretero, F. García, P. de Miguel, F. Pérez Memoria compartida Proceso A Proceso B Texto Texto Datos var2 Datos var1 2 Pila Segmento de memoria compartida Pila • Declaración independiente de variables Sistemas operativos: una visión aplicada 45 © J. Carretero, F. García, P. de Miguel, F. Pérez 23 Mutex • 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 46 © 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 Proceso ligero A Proceso ligero B lock mutex lock mutex Sección crítica obtiene mutex unlock mutex Proceso ligero ejecutando Proceso ligero bloqueado Punto de sincronización Sistemas operativos: una visión aplicada 47 © J. Carretero, F. García, P. de Miguel, F. Pérez 24 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 48 © J. Carretero, F. García, P. de Miguel, F. Pérez Uso de colas y puertos Proceso cliente Proceso cliente send Proceso cliente Proceso cliente receive send Puerto mensaje mensaje Cola de mensajes Comunicación con colas de mensajes Sistemas operativos: una visión aplicada Comunicación con puertos 49 © J. Carretero, F. García, P. de Miguel, F. Pérez 25 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 50 © 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 51 © J. Carretero, F. García, P. de Miguel, F. Pérez 26 Servidor multithread con colas de mensajes Proceso cliente Proceso cliente Cola del cliente Cola del cliente petición petición Proceso servidor respuesta Cola del servidor respuesta Creación del thread Creación del thread Thread que sirve la petición Sistemas operativos: una visión aplicada Thread que sirve la petición 52 © 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 */ 53 © J. Carretero, F. García, P. de Miguel, F. Pérez 27 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 54 © 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 55 © J. Carretero, F. García, P. de Miguel, F. Pérez 28 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 56 © 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 57 © J. Carretero, F. García, P. de Miguel, F. Pérez 29 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 58 © 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 59 © J. Carretero, F. García, P. de Miguel, F. Pérez 30 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 60 © 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 Tabla de procesos BCP1 BCP2 BCP3 BCP4 BCP5 BCP6 BCP7 BCP8 BCP9 BCP10 BCP11 BCP12 Estado PID Bloq. 7 0 Cola asociada al semáforo 6 1 Ejec. 11 5 0 8 9 7 (a) Tabla de procesos BCP1 BCP2 BCP3 BCP4 BCP5 BCP6 BCP7 BCP8 BCP9 BCP10 BCP11 BCP12 Estado PID Cola asociada al semáforo 0 Bloq. 7 7 6 1 Bloq. 11 Bloq. 5 0 8 9 11 (b) Sistemas operativos: una visión aplicada 61 © J. Carretero, F. García, P. de Miguel, F. Pérez 31 Implementación de la espera pasiva • Operaciones para desbloquear a un proceso bloqueado en un semáforo Tabla de procesos BCP1 BCP2 BCP3 BCP4 BCP5 BCP6 BCP7 BCP8 BCP9 BCP10 BCP11 BCP12 Estado PID 0 Cola asociada al semáforo Bloq. 7 7 6 Bloq. 11 1 Bloq. 5 0 8 9 11 (a) Tabla de procesos BCP1 BCP2 BCP3 BCP4 BCP5 BCP6 BCP7 BCP8 BCP9 BCP10 BCP11 BCP12 Estado PID Cola asociada al semáforo 0 Listo 7 6 Bloq. 11 1 Bloq. 5 0 8 9 11 (b) Sistemas operativos: una visión aplicada 62 © 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 63 © J. Carretero, F. García, P. de Miguel, F. Pérez 32 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 64 © 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 65 © J. Carretero, F. García, P. de Miguel, F. Pérez 33 Implementación de un semáforo con test-and-set 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; } 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 66 © 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) 67 © J. Carretero, F. García, P. de Miguel, F. Pérez 34 Ejemplo de interbloqueo P A B Q Sistemas operativos: una visión aplicada 68 © 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 69 © J. Carretero, F. García, P. de Miguel, F. Pérez 35 Servicios Posix: 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 70 © J. Carretero, F. García, P. de Miguel, F. Pérez Servicios Posix: 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 71 © J. Carretero, F. García, P. de Miguel, F. Pérez 36 Servicios Posix: 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 72 ; © J. Carretero, F. García, P. de Miguel, F. Pérez Servicios Posix: 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 73 © J. Carretero, F. García, P. de Miguel, F. Pérez 37 Servicios Posix: 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 74 © J. Carretero, F. García, P. de Miguel, F. Pérez Wind32: Servicios de sincronización • DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); • DWORD WaitForMultipleObjects(DWORD nCount, CONST HANDLE *lpHandles, BOOL fWaitFail, DWORD dwMilliseconds); Sistemas operativos: una visión aplicada 75 © J. Carretero, F. García, P. de Miguel, F. Pérez 38 Wind32: Tuberías • Crear una tubería sin nombre – BOOL CreatePipe(PHANDLE phRead, PHANDLE phWrite, LPSECURITY_ATTRIBUTES lpsa, DWORD cbPipe); • Crear una tubería con nombre – HANDLE CreateNamedPipe (LPCTSTR lpszPipeName, DWORD fdwOpenMode, DWORD fdwPipeMode, DWORD nMaxInstances, DWORD cbOutBuf, DWORD cbInBuf, DWORD dwTimeOut, LPSECURITY_ATTRIBUTES lpsa); • Abrir una tubería con nombre – HANDLE CreateFile(LPCSTR lpFileName, DWORD dwDesiredAccess,DWORD dwShareMode, LPVOID lpSecurityAttributes, DWORD CreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); Sistemas operativos: una visión aplicada 76 © J. Carretero, F. García, P. de Miguel, F. Pérez Wind32: Tuberías (II) • Cerrar una tubería con nombre – BOOL CloseHandle (HANDLE hfile); • Leer de una tubería – BOOL ReadFile (HANDLE hFile, LPVOID lpBuffer, DWORD nBytes, LPDWORD lpnBytes, LPOVERLAPPED lpOverlapped); • Escribir en una tubería – BOOL WriteFile (HANDLE hFile, LPVOID lpBuffer, DWORD nBytes, LPDWORD lpnBytes, LPOVERLAPPED lpOverlapped); Sistemas operativos: una visión aplicada 77 © J. Carretero, F. García, P. de Miguel, F. Pérez 39 Wind32: Secciones críticas • VOID InitializeCriticalSection( LPCCRITICAL_SECTION lpcsCriticalSection); • VOID DeleteCriticalSection( LPCCRITICAL_SECTION lpcsCriticalSection); • VOID EnterCriticalSection( LPCCRITICAL_SECTION lpcsCriticalSection); • VOID LeaveCriticalSection( LPCCRITICAL_SECTION lpcsCriticalSection); Sistemas operativos: una visión aplicada 78 © J. Carretero, F. García, P. de Miguel, F. Pérez Wind32: Semáforos • Crear un semáforo – HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES lpsa, LONG cSemInitial, LONG cSemMax, LPCTSTR lpszSemName); • Abrir un semáforo – HANDLE OpenSemaphore(LONG dwDesiredAccess, LONG BinheritHandle, lpszName SemName); • Operación wait – DWORD WaitForSingleObject(HANDLE hSem, DWORD dwTimeOut); • Operación signal – BOOL ReleaseSemaphore(HANDLE hSemaphore, LONG cReleaseCount, LPLONG lpPreviousCount); Sistemas operativos: una visión aplicada 79 © J. Carretero, F. García, P. de Miguel, F. Pérez 40 Wind32: Mutex y eventos • Crear un mutex – HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpsa, BOOL fInitialOwner, LPCTSTR lpszMutexName); • Abrir un mutex – HANDLE OpenMutex(LONG dwDesiredAccess, LONG BineheritHandle, lpszName SemName); • Cerrar un mutex – BOOL CloseHandle(HANDLE hObject); • Operación lock – DWORD WaitForSingleObject(HANDLE hMutex, DWORD dwTimeOut); • Operación unlonk – BOOL ReleaseMutex(HANDLE hMutex); Sistemas operativos: una visión aplicada 80 © J. Carretero, F. García, P. de Miguel, F. Pérez Wind32: Mutex y eventos (II) • Crear un evento – HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpsa, BOOL fManualReset, BOOL fInitialState, LPTCSTR lpszEventName); • Destruir un evento – BOOL CloseHandle(HANDLE hObject); • Esperar por un evento – DWORD WaitForSingleObject(HANDLE hEvent, DWORD dwTimeOut); • Notificar un evento – BOOL SetEvent(HANDLE hEvent); – BOOL PulseEvent(HANDLE hEvent); Sistemas operativos: una visión aplicada 81 © J. Carretero, F. García, P. de Miguel, F. Pérez 41