Post on 04-Jan-2016
description
Comunicación y sincronización entre procesos
María de los Santos Pérez Hernández
mperez@fi.upm.es
mperez@fi.upm.es 2
Índice
Procesos concurrentes. El problema de la sección crítica. Problemas clásicos de comunicación y
sincronización. Mecanismos de comunicación y
sincronización. Interbloqueos.
mperez@fi.upm.es 3
Referencias bibliográficas “Sistemas Operativos: una visión aplicada”
Jesús Carretero et al.McGraw Hill, 2001
“Sistemas Operativos” Willian StallingWillian Stalling Prentice Hall, 1997
“Operating System Concepts” A. Silberschatz, P. GalvinAddison-Wesley, 1998
mperez@fi.upm.es 4
Procesos concurrentes (I) 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
mperez@fi.upm.es 5
Procesos concurrentes (II)
Tipos de procesos Independientes Cooperantes
Interacción entre procesos Compiten por recursos Comparten recursos
mperez@fi.upm.es 6
Problema de la interacción entre procesos (procesos ligeros)
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
mperez@fi.upm.es 7
Ejemplo con sección crítica(procesos ligeros)
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);
}
Requisitos para ofrecer secciones críticas:
Exclusión mutua Progreso Espera limitada
mperez@fi.upm.es 8
Problema de la interacción entre procesos
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
mperez@fi.upm.es 9
Ejemplo con sección críticavoid 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;
}
Requisitos para ofrecer secciones críticas:
Exclusión mutua Progreso Espera limitada
mperez@fi.upm.es 10
Mecanismos de comunicación
Ficheros Pipes FIFOS Variables en memoria compartida Paso de mensajes Sockets
mperez@fi.upm.es 11
Mecanismos de Sincronización Construcciones de los lenguajes concurrentes
(procesos ligeros) Servicios del sistema operativo:
Señales (asincronismo) Pipes FIFOS Semáforos Mutex y variables condicionales Paso de mensajes
Las operaciones de sincronización deben ser atómicas
mperez@fi.upm.es 12
Características de los mecanismos de comunicación
Identificación Mecanismos de nombrado
Sin nombre Con nombre local Con nombre de red
Identificador interno Identificador propio Descriptor de fichero
Flujo de datos Unidireccional Bidireccional
Buffering Sin buffering Con buffering
Sincronización Síncrono (bloqueante) Asíncrono (no
bloqueante)
mperez@fi.upm.es 13
Problemas clásicos de comunicación y sincronización
ProcesoProductor
ProcesoConsum idor
M ecanism o de com unicación
Flu jo de datos
• Productor-consumidor:
EscritorLector Lector Escritor Lector
Recurso
• Lectores-escritores:
mperez@fi.upm.es 14
Problemas clásicos de comunicación y sincronización (II)
• Comunicación cliente-servidor:
Computadora Computadora
Procesocliente
S.O.
Petición
Respuesta
Procesoservidor
mperez@fi.upm.es 15
Pipes Mecanismo de comunicación y
sincronización sin nombre Sólo puede utilizarse entre los
procesos hijos del proceso que creó el pipe
int pipe(int fildes[2]); Identificación: dos descriptores
de fichero Flujo de datos: unidireccional Con buffering
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)
mperez@fi.upm.es 16
Secciones críticas con pipesvoid main(void){ int fildes[2]; /* pipe sincronización */ char c; /* carácter sincronización */
pipe(fildes); write(fildes[1], &c, 1);
/* necesario para entrar en la seccioncrítica la primera vez */
if (fork() == 0) { /* proceso hijo */ for(;;)
{ read(fildes[0], &c, 1);
/* entrada sección crítica */ /* sección crítica */ write(fildes[1], &c, 1);
/* salida seccion crítica */ } }
else /* proceso padre */
{
for(;;)
{
read(fildes[0], &c, 1); /* entrada sección
crítica*/
/* sección crítica */
write(fildes[1], &c, 1); /* salida sección crítica
*/
}
}
}
mperez@fi.upm.es 17
Productor-consumidor con pipes
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) /* productor */ { for(;;) { /* producir dato_p */ write(fildes[1], dato_p,
4*sizeof(int)); } }
else /* consumidor */
{
for(;;)
{
read(fildes[0], &dato_c,
sizeof(int));
/* consumir dato */
}
}
}Proceso
hijoProceso
padre
pipe
SO
write read
Flujo de datos
mperez@fi.upm.es 18
Ejecución de mandatos con pipes
/* programa que ejecuta el mandato “ls | wc” */void main(void) { int fd[2]; pid_t pid;
if (pipe(fd) < 0) { perror(``pipe''); exit(1); }
pid = fork(); switch(pid) { case -1: /* error */ perror(``fork''); exit(1);
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''); } }
mperez@fi.upm.es 19
Ejecución de mandatos con pipes (II)
Proceso
S TD INS TD O UT
pipe(fd)
fork()
Proceso
S TD INS TD O UTfd[0]fd [1 ]
pipe
red irección
P roceso h ijo
S TD INS TD O UTfd[0]fd [1 ]
P roceso
S TD INS TD O UTfd[0]fd [1 ]
pipe
exec
Proceso hijo
S TD IN
S TD O UT
Proceso
S TD IN
S TD O UTpipe
Proceso hijo
S TD IN
S TD O UT
Proceso
ls wc
S TD IN
S TD O UTpipe
mperez@fi.upm.es 20
FIFOS
Igual que los pipes Mecanismo de comunicación y
sincronización con nombre Misma máquina
Servicios
mkfifo(char *name,mode_t mode); Crea un FIFO con nombre name
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()
mperez@fi.upm.es 21
Semáforos Mecanismo de sincronización Misma máquina Objeto con un valor entero Dos operaciones atómicas
s_espera(s) { s = s - 1; if (s < 0) Bloquear al proceso }
s_abre(s) { s = s + 1; if (s <= 0) Desbloquear a un
proceso bloqueado }
Secciones críticas con semáforos:
s_espera(s); /* entrada en la seccion critica */
/* seccion critica */
s_abre(s); /* salida de la
seccion critica */
El semáforo debe tener valor inicial 1
mperez@fi.upm.es 22
Semáforos POSIX 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 s_espera sobre un semáforo. int sem_post(sem_t *sem);
Realiza la operación s_abre sobre un semáforo.
mperez@fi.upm.es 23
Productor-consumidor con semáforos (buffer acotado y circular)
Consum idor
Productor
mperez@fi.upm.es 24
Productor-consumidor con semáforos (II)
#define MAX_BUFFER 1024 #define DATOS_A_PRODUCIR 100000sem_t elementos; /* elementos buffer */sem_t huecos; /* huecos buffer */sem_t mutex; /* controla excl.mutua */
int buffer[MAX_BUFFER];/*buffer común*/void main(void){ pthread_t th1, th2; /* id. threads*//* inicializar los semaforos */ sem_init(&elementos, 0, 0); sem_init(&huecos, 0, MAX_BUFFER);
sem_init(&mutex, 0, 1);
/* 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); sem_destroy(&mutex);
exit(0);}
mperez@fi.upm.es 25
Productor-consumidor con semáforos (III)void Productor(void) /* código del productor */{ int pos = 0; /* posición 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 */ sem_wait(&mutex); /* entra en la sección crítica */ buffer[pos] = dato; pos = (pos + 1) % MAX_BUFFER; sem_post(&mutex); /* deja la sección crítica */ sem_post(&elementos); /* un elemento más */ } pthread_exit(0);}
mperez@fi.upm.es 26
Productor-consumidor con semáforos (IV)void Consumidor(void) /* código del Consumidor */{ int pos = 0; int dato; int i;
for(i=0; i < DATOS_A_PRODUCIR; i++ ) { sem_wait(&elementos); /* un elemento menos */ sem_wait(&mutex); /* entra en la sección crítica */ dato = buffer[pos]; pos = (pos + 1) % MAX_BUFFER; sem_post(&mutex); /* deja la sección crítica */ sem_post(&huecos); /* un hueco más */ /* consumir dato */ } pthread_exit(0);}
mperez@fi.upm.es 27
Lectores-escritores con semáforos
int dato = 5; /* recurso */int n_lectores = 0;/* num. Lectores */sem_t sem_lec; /* acceso n_lectores */sem_t mutex; /* 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, NULL, Lector, NULL);
pthread_create(&th2, NULL, Escritor, NULL);
pthread_create(&th3, NULL, Lector, NULL);
pthread_create(&th4, NULL, Escritor, NULL);
pthread_join(th1, NULL); pthread_join(th2, NULL); pthread_join(th3, NULL); pthread_join(th4, NULL);
/*cerrar todos los semaforos*/ sem_destroy(&mutex); sem_destroy(&sem_lec);
exit(0);}
mperez@fi.upm.es 28
Lectores-escritores con semáforos (II)void Lector(void) /* código 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);}
mperez@fi.upm.es 29
Lectores-escritores con semáforos (III)
void Escritor(void) /* código del escritor */
{
sem_wait(&mutex);
dato = dato + 2; /* modificar el recurso */
sem_post(&mutex);
pthread_exit(0);
}
mperez@fi.upm.es 30
Objetos de memoria compartida Permiten crear un
segmento de memoria compartido entre procesos de la misma máquina.
Declaración independiente de variables
D atos
Texto
Proceso A Proceso B
P ila
Texto
D atos
P ilaSegm ento de m em oriacom partida
var1
*var1=2
2
var2
mperez@fi.upm.es 31
Servicios int shm_open(char *name, int oflag,
mode_t mode); Crea un objeto de memoria a compartir entre procesos
int shm_open (char *name, int oflag); Sólo apertura
int close(int fd); Cierre. El objeto persiste hasta que es cerrado por el último
proceso. int shm_unlink(const char *name);
Borra una zona de memoria compartida. void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);
Establece una proyección entre el espacio de direcciones de un proceso y un descriptor de fichero u objeto de memoria compartida.
mperez@fi.upm.es 32
Servicios (II) len especifica el número de bytes a proyectar. prot el tipo de acceso (lectura, escritura o ejecución).
PROT_READ, PROT_WRITE, PROT_EXEC o PROT_NONE. flags especifica información sobre el manejo de los
datos proyectados (compartidos, privado, etc.). MAP_SHARED, MAP_PRIVATE, ...
fildes representa el descriptor de fichero del fichero o descriptor del objeto de memoria a proyectar.
off desplazamiento dentro del fichero a partir de cual se realiza la proyección.
void munmap(void *addr, size_t len); Desproyecta parte del espacio de direcciones de un
proceso comenzando en la dirección addr. El tamaño de la región a desproyectar es len.
mperez@fi.upm.es 33
Productor-consumidor con objetos de memoria compartida Productor:
Crea la zona de memoria compartida (shm_open)
Le asigna espacio (ftruncate) int ftruncate (int fd, size_t len);
Proyecta la zona de memoria compartida en su espaciode direcciones (mmap)
Utiliza la zona de memoria compartida Desproyecta la zona de memoria compartida Cierra y borra el objeto de memoria compartida
mperez@fi.upm.es 34
Productor-consumidor con objetos de memoria compartida (II)
Consumidor: Debe esperar a que la zona de memoria
compartida este creada Abre la zona de memoria compartida
(shm_open) Proyecta la zona de memoria compartida en
su espacio de direcciones (mmap) Utiliza la zona de memoria compartida Cierra el objeto de memoria compartida
mperez@fi.upm.es 35
Código del productor#define MAX_BUFFER 1024 /* tamaño del buffer */#define DATOS_A_PRODUCIR 100000 /* datos a producir */sem_t *elementos; /* elementos en el buffer */sem_t *huecos; /* huecos en el buffer */sem_t *mutex; /* acceso al buffer */
void main(int argc, char *argv[]){ int shd; int *buffer; /* buffer común */
/* el productor crea el segmento de memoria compartida */ shd = shm_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 */
mperez@fi.upm.es 36
Código del productor (II)buffer = (int *) mmap(NULL, MAX_BUFFER * sizeof(int),
PROT_WRITE, MAP_SHARED, shd, 0);
/* El productor crea los semáforos */ elementos = sem_open("ELEMENTOS", O_CREAT, 0700, 0); huecos = sem_open("HUECOS", O_CREAT, 0700, MAX_BUFFER); mutex = sem_open("MUTEX", O_CREAT, 0700, 1); Productor(buffer);
/* desproyectar el buffer compartido */ munmap(buffer, MAX_BUFFER * sizeof(int)); close(shd); /* cerrar el objeto de memoria compartida */ shm_unlink("BUFFER"); /* borrar el objeto de memoria */
sem_close(elementos); sem_close(huecos); sem_close(mutex); sem_unlink("ELEMENTOS"); sem_unlink("HUECOS"); sem_unlink("MUTEX"); }
mperez@fi.upm.es 37
Código del productor (III)void Productor(int *buffer){ /* código del productor */ int pos = 0; /* posición 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 */
sem_wait(mutex); buffer[pos] = dato; pos = (pos + 1) % MAX_BUFFER;
sem_post(mutex); sem_post(elementos); /* un elemento más */ } return;}
mperez@fi.upm.es 38
Código del consumidor#define MAX_BUFFER 1024 /* tamaño del buffer */#define DATOS_A_PRODUCIR 100000 /* datos a producir */
sem_t *elementos; /* elementos en el buffer */sem_t *huecos; /* huecos en el buffer */sem_t *mutex; /* acceso al buffer */void main(int argc, char *argv[]){ int shd; int *buffer; /* buffer común */
/* el consumidor abre el segmento de memoria compartida */ shd = shm_open("BUFFER", O_RDONLY);
/* proyectar el objeto de memoria compartida en el espacio de direcciones del productor */
mperez@fi.upm.es 39
Código del consumidor (II)buffer = (int *) mmap(NULL, MAX_BUFFER * sizeof(int), PROT_READ, MAP_SHARED, shd, 0);
/* El consumidor abre los semáforos */ elementos = sem_open("ELEMENTOS", 0); huecos = sem_open("HUECOS", 0);
mutex = sem_open("MUTEX", 0); Consumidor(buffer);
/* desproyectar el buffer compartido */ munmap(buffer, MAX_BUFFER * sizeof(int)); close(shd); /* cerrar el objeto de memoria compartida */
/* cerrar los semáforos */ sem_close(elementos); sem_close(huecos); sem_close(mutex); }
mperez@fi.upm.es 40
Código del consumidor (III)void Consumidor(char *buffer) /*código del Consumidor*/{ int pos = 0; int i, dato;
for(i=0; i < DATOS_A_PRODUCIR; i++ ) { sem_wait(elementos); /* un elemento menos */
sem_wait(mutex); dato = buffer[pos]; pos = (pos + 1) % MAX_BUFFER;
sem_post(mutex); sem_post(huecos); /* un hueco mas */ printf("Consume %d \n", dato); /* consumir dato */ } return;}
mperez@fi.upm.es 41
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.
mperez@fi.upm.es 42
Secciones críticas con mutexlock(m); /* entrada en la sección crítica */
/* sección crítica */
unlock(s); /* salida de la sección crítica */ La operación unlock debe realizarla el proceso ligero que
ejecutó lock P roceso ligero A
lock m utex
Seccióncrítica
P roceso ligero B
lock m utex
unlock m utex obtiene m utex
Proceso ligero e jecutando
Proceso ligero b loqueado
Punto de sincronización
mperez@fi.upm.es 43
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
Proceso ligero B
Proceso ligero A
w a itunlock mutex
Adquiere el mutex
Adquiere el mutex
Se compite por el mutex
locklock
un lock
Proceso ligero bloqueado esperando signal
Proceso ligero b loqueado esperando unlock
signa l
mperez@fi.upm.es 44
Uso de mutex y variables condicionales
Proceso ligero A:
lock(mutex); /* acceso al
recurso *//* codigo de la sección
crítica */while (condicion == FALSE)
wait(condition, mutex);/* resto de la sección
crítica */unlock(mutex);
Proceso ligero B
lock(mutex); /* acceso al recurso */
/* código de la sección crítica */
/* se modifica la condicción y ésta se hace TRUE */
condicion = true;signal(condition, mutex);unlock(mutex);
Importante utilizar while
mperez@fi.upm.es 45
Servicios 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. int pthread_cond_destroy(pthread_cond_t *cond);
Destruye una variable condicional.
mperez@fi.upm.es 46
Servicios (II) 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.
mperez@fi.upm.es 47
Productor-consumidor con mutex
#define MAX_BUFFER 1024 /* tamaño del buffer */#define DATOS_A_PRODUCIR 100000 /* datos a producir */pthread_mutex_t mutex; /* mutex para controlar el
acceso al buffer compartido */pthread_cond_t no_lleno; /* llenado del buffer */pthread_cond_t no_vacio; /* vaciado del buffer */int n_elementos; /* elementos en el buffer */int buffer[MAX_BUFFER]; /* buffer común */
main(int argc, char *argv[]){
pthread_t th1, th2;
pthread_mutex_init(&mutex, NULL);
mperez@fi.upm.es 48
Productor-consumidor con mutex (II)
pthread_cond_init(&no_lleno, NULL);pthread_cond_init(&no_vacio, NULL);
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);}
mperez@fi.upm.es 49
Productor-consumidor con mutex (III)
void Productor(void) { /* código del productor */ int dato, i ,pos = 0; 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);/* bloqueo */ buffer[pos] = dato; pos = (pos + 1) % MAX_BUFFER; n_elementos ++; pthread_cond_signal(&no_vacio);/* buffer no vacío */ pthread_mutex_unlock(&mutex); } pthread_exit(0);}
mperez@fi.upm.es 50
Productor-consumidor con mutex (IV)
void Consumidor(void) { /* código del consumidor */ 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 vacío */ pthread_cond_wait(&no_vacio, &mutex);/* bloqueo */ 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);}
mperez@fi.upm.es 51
Lectores-escritores con mutex
int dato = 5; /* recurso */
int n_lectores = 0; /* numero de lectores */
pthread_mutex_t mutex; /* control del acceso a dato */
pthread_mutex_t mutex_lectores; /* control de n_lectores */
main(int argc, char *argv[])
{
pthread_t th1, th2, th3, th4;
pthread_mutex_init(&mutex, NULL);
pthread_mutex_init(&mutex_lectores, NULL);
mperez@fi.upm.es 52
Lectores-escritores con mutex (II)pthread_create(&th1, NULL, Lector, NULL);pthread_create(&th2, NULL, Escritor, NULL);pthread_create(&th3, NULL, Lector, NULL);pthread_create(&th4, NULL, Escritor, NULL);
pthread_join(th1, NULL);pthread_join(th2, NULL);pthread_join(th3, NULL);pthread_join(th4, NULL);
pthread_mutex_destroy(&mutex);pthread_mutex_destroy(&mutex_lectores);
exit(0);}
mperez@fi.upm.es 53
Lectores-escritores con mutex (III)
void Lector(void) { /* código 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);}
mperez@fi.upm.es 54
Lectores-escritores con mutex (IV)
void Escritor(void) /* código del escritor */
{
pthread_mutex_lock(&mutex);
dato = dato + 2; /* modificar el recurso */
pthread_mutex_unlock(&mutex);
pthread_exit(0);
}
mperez@fi.upm.es 55
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(origen, mensaje):
recibe un mensaje del proceso origen
Múltiples soluciones Paso de mensajes en POSIX
(colas de mensajes): Misma máquina
(uniprocesador, multiprocesador o multicomputador)
Mecanismo de nombrado: con nombre local
Identificación: identificador especial
Buffering: Sí Unidireccional Sincronización: bloqueante y
no bloqueante
mperez@fi.upm.es 56
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.
mperez@fi.upm.es 57
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 (el de mayor prioridad). Almacena la prioridad del mensaje en el parámetro prio;
Recepción bloqueante o no.
mperez@fi.upm.es 58
Secciones críticas con colas de mensajes
void main(void){ mqd_t mutex; /* cola de mensajes para sincronizar
acceso a la sección crítica */ struct mq_attr attr; /*atributos cola de mensajes*/ char c; /* carácter para sincronizar */
attr.mq_maxmsg = 1; /* número máximo mensajes */ attr.mq_msgsize = 1; /* tamaño mensaje */ mutex = mq_open(``MUTEX'', O_CREAT|O_RDWR, 0777,
&attr);
mq_send(mutex, &c, 1, 0); /* entrar en la sección crítica la primera vez */
mperez@fi.upm.es 59
Secciones críticas con colas de mensajes (II)
if (fork() == 0) { /* proceso hijo */ for(;;){ mq_receive(mutex, &c, 1, NULL);/*entrada sec. crítica */ /* sección crítica */ mq_send(mutex, &c, 1, 0); /* salida sec. critica */ } }else { /* proceso padre */ for(;;){ mq_receive(mutex, &c, 1, NULL);/*entrada sec. crítica */ /* sección crítica */ mq_send(mutex, &c, 1, 0); /* salida sec. critica */ } }}
mperez@fi.upm.es 60
Productor-consumidor con colas de mensajes
#define MAX_BUFFER 1024 /* tamaño del buffer */#define DATOS_A_PRODUCIR 100000 /*datos a producir*/
mqd_t almacen; /* cola de mensaje para almacenamientode datos */
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);
mperez@fi.upm.es 61
Productor-consumidor con colas de mensajes (II) if (almacen == -1) { perror("mq_open"); exit(1); }
if (fork() == 0) /* proceso hijo */ Productor(); else /* proceso padre */ Consumidor();
exit(0);
}
mperez@fi.upm.es 62
Productor-consumidor con colas de mensajes (III)
void Productor(void) { int dato; int i;
for(i=0;i < DATOS_A_PRODUCIR; i++ ){
/* producir dato */ dato = i; printf("Produce %d \n",
dato); mq_send(almacen, &dato,
sizeof(int), 0); } return;}
void Consumidor(void){ int dato; int i; for(i=0;
i < DATOS_A_PRODUCIR; i++ ){
mq_receive(almacen, &dato, sizeof(int), NULL);
/* consumir dato */ printf("Consume
%d\n", dato); } return;}
mperez@fi.upm.es 63
Lectores-escritores con colas de mensajes
struct peticion_t{ int tipo; char cola[100];};void lector(char *lector) { /* identificador lector */ mqd_t controlador; /* cola proceso controlador */ mqd_t cola_local; /* cola para el lector */ struct mq_attr attr; struct peticion_t peticion;
attr.mq_maxmsg = 1; attr.mq_msgsize = sizeof(struct peticion_t);
controlador = mq_open("CONTROLADOR", O_RDWR);cola_local = mq_open(lector, O_CREAT|O_RDWR, 0777, &attr);
mperez@fi.upm.es 64
Lectores-escritores con colas de mensajes (II)
peticion.tipo = READ;strcpy(peticion.cola, lector);
/* solicitud de lectura al controlador */ mq_send(controlador, &peticion, sizeof(peticion), 0); mq_receive(cola_local, &peticion, sizeof(peticion), NULL);
/* leer */ peticion.tipo = OK; /* fin de lectura */ mq_send(cola_local, &peticion, sizeof(peticion), 0); mq_close(cola_local); mq_close(controlador);
mq_unlink(lector);} El proceso escritor es similar.
mperez@fi.upm.es 65
Lectores-escritores con colas de mensajes (III)
void Controlador(void){ mqd_t controlador; /* cola del proceso controlador */ struct mq_attr attr; struct peticion_t peticion;
attr.mq_maxmsg = 1; attr.mq_msgsize = sizeof(struct peticion_t);
controlador = mq_open("CONTROLADOR", O_CREAT|O_RDWR, 0777, &attr);
for (;;) { mq_receive(controlador, &peticion, sizeof(peticion), 0); if (peticion.tipo == READ) crear_thread(do_read, &peticion); else crear_thread(do_write, &peticion); }}
mperez@fi.upm.es 66
Lectores-escritores con colas de mensajes (IV)
void do_read(struct peticion_t *pet) { mqd_t cola_local;
struct peticion_t peticion;
/* abre la cola del proceso que realiza la peticion */
cola_local = mq_open(pet->cola, O_RDWR);
pthread_mutex_lock(mutex); n_lectores ++; peticion.tipo = OK; mq_send(cola_local, peticion,
sizeof(peticion), 0); mq_receive(cola_local, peticion,
sizeof(peticion), NULL); n_lectores --; if (n_lectores == 0) pthread_cond_signal(&no_lectores); pthread_mutex_unlock(mutex);}
void do_write(struct peticion_t *pet) { mqd_t cola_local;
struct peticion_t peticion;
/* abre la cola del proceso que realiza la peticion */
cola_local = mq_open(pet->cola, O_RDWR);
pthread_mutex_lock(mutex); while (n_lectores > 0) pthread_cond_wait(&no_lectores,
&mutex);
peticion.tipo = OK; mq_send(cola_local, peticion,
sizeof(peticion), 0); mq_receive(cola_local, peticion,
sizeof(peticion), NULL); pthread_mutex_unlock(mutex);}
mperez@fi.upm.es 67
Servidor multithread con colas de mensajes
Proceso c liente
C ola de l c lien te
Proceso c liente
C ola de l c lien te
C ola de l serv idor
Proceso serv idor
petic ión
petic ión
respuestarespuesta
C reación de lth read
C reación de lth read
Thread quesirve la petic ión
Thread quesirve la petic ión
mperez@fi.upm.es 68
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 var. condicionales protección de 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; /* cola del servidor */ struct mensaje mess; /* mensaje a recibir */ struct mq_attr q_attr; /* atributos de la cola */ pthread_attr_t t_attr; /* atributos de los threads */
q_attr.mq_maxmsg = 20;q_attr.mq_msgsize = sizeof(struct mensaje);
mperez@fi.upm.es 69
Servidor multithread con colas de mensajes (III)
q_servidor = mq_open("SERVIDOR", O_CREAT|O_RDONLY, 0700, &q_attr);pthread_mutex_init(&mutex_mensaje, NULL);
pthread_cond_init(&cond, NULL); pthread_attr_init(&t_attr); pthread_attr_setscope(&t_attr,PTHREAD_SCOPE_SYSTEM); pthread_attr_setdetachstate(&t_attr, PTHREAD_CREATE_DETACHED); while (TRUE) { mq_receive(q_servidor, &mess, sizeof(struct mensaje), NULL);
pthread_create(&thid, &t_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); }}
mperez@fi.upm.es 70
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 respuesta; /* 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*/
mensaje_no_copiado = FALSE; pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex_mensaje);
/* ejecutar la petición del cliente y preparar respuesta */
/* responder al cliente a su cola */ q_cliente = mq_open(mensaje_local.cliente, O_WRONLY); mqsend(q_cliente, (char *) &respuesta, sizeof(respuesta), 0); mq_close(q_cliente); pthread_exit(0);}
mperez@fi.upm.es 71
Ejemplo de código clientestruct mensaje {/* estructura mensaje */ char buffer[1024]; /* datos envío */ char cliente[256]; /* cola cliente */};
void main(void){ mqd_t q_servidor; /* cola servidor */ mqd_t q_cliente; /* cola cliente */ struct mq_attr attr; /* atr. cola */ struct mensaje peticion; /* peticion al
servidor */ struct mensaje respuesta; /* respuesta
del servidor */
attr.mq_maxmsg = 1; attr.mq_msgsize =
sizeof(struct mensaje);
q_cliente = mq_open("CLIENTE", O_CREAT|O_RDONLY, 0700, 0);
q_servidor = mq_open("SERVIDOR", O_WRONLY);
/* preparar peticion */ mq_send(q_servidor, &peticion,
sizeof(struct mensaje), 0);
/* esperar respuesta */ mq_receive(q_cliente, &respuesta,
sizeof(struct mensaje), NULL);
mq_close(q_servidor); mq_close(q_cliente); mq_unlink("CLIENTE");
exit(0);}
mperez@fi.upm.es 72
Sockets
Permiten comunicar: Procesos del mismo computador Procesos conectados a través de una red
Tipos de direcciones: Direcciones locales: dominio UNIX Direcciones de red (TCP/IP)
Dirección de red (dirección IP) Puerto
mperez@fi.upm.es 73
Sockets (II)
merlin138.100.240.50
conan138.100.240.20
Red de interconexión
ftp21
telnet 23
patan138.100.240.36
zipi138.100.240.2
batman138.100.240.32
mperez@fi.upm.es 74
Resumen Pipes:
Mecanismo de comunicación y sincronización
Mecanismo de nombrado: sin nombre
Identificación: descriptor de fichero
Flujo de datos: unidireccional
Buffering: Sí Sincronización:
Si pipe vacío read() se bloquea
Si pipe lleno write() se bloquea
FIFOS: Mecanismo de comunicación
y sincronización Mecanismo de nombrado:
con nombre local Identificación: descriptor de
fichero Flujo de datos:
unidireccional Buffering: Sí Sincronización:
Si FIFO vacío read() se bloquea
Si FIFO lleno write() se bloquea
mperez@fi.upm.es 75
Resumen (II) Semáforos POSIX:
Mecanismo de sincronización
Mecanismo de nombrado: sin nombre y con nombre local
Identificación: variable de tipo semáforo
Mutex y variables condicionales:
Mecanismo de sincronización adecuado para procesos ligeros
Mecanismo de nombrado: sin nombre
Identificación: variables
Colas de mensajes POSIX: Mecanismo de
comunicación y sincronización
Mecanismo de nombrado: con nombre local
Identificación: identificador especial
Buffering: Sí Unidireccional Sincronización: bloqueante
y no bloqueante
mperez@fi.upm.es 76
Resumen (III) Objetos de memoria compartida:
Mecanismo de comunicación Mecanismo de nombrado: con nombre local Identificación: identificador especial
Sockets: Mecanismo de comunicación y sincronización Mecanismo de nombrado: con nombre de red Identificación: descriptor de fichero Buffering: Sí Sincronización: bloqueante y no bloqueante
mperez@fi.upm.es 77
Interbloqueos Bloqueo permanente de un
conjunto de procesos que compiten por los recursos del sistema o se comunican entre sí.
Ejemplo: Si S y Q con semáforos con valor inicial
P0 P1 s_espera(S) s_espera(Q)
s_espera(Q) s_espera(S)
. .
s_abre(S) s_abre(Q)
s_abre(Q) s_abre(S)
Ejemplo: Si C1 y C2 son dos colas de mensajes
P0 P1
receive(C1, M) receive(C2, N)
. .
send(C2, M) send(C1, N)
Condiciones del interbloqueo:
Exclusión mutua Retención y espera No apropiación Espera circular
mperez@fi.upm.es 78
Ejemplo Ejemplo: Si P y Q son semáforos con valor inicial
1 A B s_espera(P) s_espera(Q)
s_espera(Q) s_espera(P)
Interbloqueo
Q
P
BA
mperez@fi.upm.es 79
Métodos para tratar con los interbloqueos
Ignorarlos UNIX Solucionarlos:
Métodos para prevenir los interbloqueos Métodos para evitar los interbloqueos Métodos para detectar los interbloqueos