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

Post on 20-Apr-2015

15 views 4 download

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

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