Comunicación y sincronización entre procesos María de los Santos Pérez Hernández...

79
Comunicación y sincronización entre procesos María de los Santos Pérez Hernández [email protected]

Transcript of Comunicación y sincronización entre procesos María de los Santos Pérez Hernández...

Comunicación y sincronización entre procesos

María de los Santos Pérez Hernández

[email protected]

[email protected] 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.

[email protected] 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

[email protected] 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

[email protected] 5

Procesos concurrentes (II)

Tipos de procesos Independientes Cooperantes

Interacción entre procesos Compiten por recursos Comparten recursos

[email protected] 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

[email protected] 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

[email protected] 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

[email protected] 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

[email protected] 10

Mecanismos de comunicación

Ficheros Pipes FIFOS Variables en memoria compartida Paso de mensajes Sockets

[email protected] 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

[email protected] 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)

[email protected] 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:

[email protected] 14

Problemas clásicos de comunicación y sincronización (II)

• Comunicación cliente-servidor:

Computadora Computadora

Procesocliente

S.O.

Petición

Respuesta

Procesoservidor

[email protected] 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)

[email protected] 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

*/

}

}

}

[email protected] 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

[email protected] 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''); } }

[email protected] 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

[email protected] 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()

[email protected] 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

[email protected] 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.

[email protected] 23

Productor-consumidor con semáforos (buffer acotado y circular)

Consum idor

Productor

[email protected] 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);}

[email protected] 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);}

[email protected] 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);}

[email protected] 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);}

[email protected] 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);}

[email protected] 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);

}

[email protected] 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

[email protected] 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.

[email protected] 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.

[email protected] 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

[email protected] 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

[email protected] 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 */

[email protected] 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"); }

[email protected] 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;}

[email protected] 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 */

[email protected] 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); }

[email protected] 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;}

[email protected] 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.

[email protected] 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

[email protected] 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

[email protected] 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

[email protected] 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.

[email protected] 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.

[email protected] 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);

[email protected] 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);}

[email protected] 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);}

[email protected] 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);}

[email protected] 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);

[email protected] 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);}

[email protected] 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);}

[email protected] 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);

}

[email protected] 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

[email protected] 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.

[email protected] 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.

[email protected] 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 */

[email protected] 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 */ } }}

[email protected] 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);

[email protected] 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);

}

[email protected] 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;}

[email protected] 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);

[email protected] 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.

[email protected] 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); }}

[email protected] 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);}

[email protected] 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

[email protected] 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);

[email protected] 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); }}

[email protected] 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);}

[email protected] 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);}

[email protected] 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

[email protected] 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

[email protected] 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

[email protected] 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

[email protected] 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

[email protected] 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

[email protected] 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

[email protected] 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