Comunicación y sincronización
entre procesos
María de los Santos Pérez Hernández
[email protected]
Í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]
2
Referencias bibliográficas



“Sistemas Operativos: una visión aplicada”
Jesús Carretero et al.
McGraw Hill, 2001
“Sistemas Operativos” Willian Stalling
Willian Stalling
Prentice Hall, 1997
“Operating System Concepts”
A. Silberschatz, P. Galvin
Addison-Wesley, 1998
[email protected]
3
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]
4
Procesos concurrentes (II)

Tipos de procesos



Independientes
Cooperantes
Interacción entre procesos


Compiten por recursos
Comparten recursos
[email protected]
5
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]
6
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

Requisitos para
ofrecer secciones
críticas:



Exclusión mutua
Progreso
Espera limitada
pthread_exit(0);
}
[email protected]
7
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]
8
Ejemplo con sección crítica
void ingresar(char *cuenta,
int cantidad)
{
int saldo, fd;

fd = open(cuenta, O_RDWR);
Entrada en la sección crítica
read(fd, &saldo, sizeof(int));
saldo = saldo + cantidad;
lseek(fd, 0, SEEK_SET);
write(fd, &saldo, sizeof(int));
Salida de la sección crítica
Requisitos para
ofrecer secciones
críticas:



Exclusión mutua
Progreso
Espera limitada
close(fd);
return;
}
[email protected]
9
Mecanismos de comunicación






Ficheros
Pipes
FIFOS
Variables en memoria compartida
Paso de mensajes
Sockets
[email protected]
10
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]
11
Características de los
mecanismos de comunicación

Identificación

Flujo de datos
Mecanismos de nombrado





Sin nombre
Con nombre local
Con nombre de red



Buffering

Identificador interno


Identificador propio
Descriptor de fichero


Unidireccional
Bidireccional
Sin buffering
Con buffering
Sincronización


[email protected]
Síncrono (bloqueante)
Asíncrono (no bloqueante)
12
Problemas clásicos de
comunicación y sincronización
• Productor-consumidor:
P roceso
C onsum idor
P roceso
P roductor
• Lectores-escritores:
Lector
Lector
E scritor
Lector
E scritor
Flujo de
datos
M ecanism o de
com unicación
R ecurso
[email protected]
13
Problemas clásicos de comunicación
y sincronización (II)
• Comunicación cliente-servidor:
Computadora
Computadora
Petición
Proceso
cliente
Proceso
servidor
S.O.
Respuesta
[email protected]
14
Pipes



Mecanismo de comunicación y
sincronización sin nombre
Sólo puede utilizarse entre los
procesos hijos del proceso que creó el
pipe
read(fildes[0],buffer,n)





int pipe(int fildes[2]);



Identificación: dos descriptores de
fichero
Flujo de datos: unidireccional
Con buffering

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 vacío  se bloquea el
lector
Pipe con p bytes 
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]
15
Secciones críticas con pipes
void main(void)
else
{
{
int fildes[2];
/* pipe sincronización */
char c; /* carácter sincronización */
pipe(fildes);
write(fildes[1], &c, 1);
/* necesario para entrar en la seccion
crí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 */
}
}
[email protected]
/* 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 */
}
16
Productor-consumidor con pipes
void main(void)
{
int fildes[2];
int dato_p[4];
int dato_c;
else
{
/* pipe para comunicar y
sincronizar */
/* datos a producir */
/* dato a consumir */
/* consumidor */
for(;;)
{
read(fildes[0], &dato_c,
sizeof(int));
/* consumir dato */
pipe(fildes);
if (fork() == 0) /* productor */
{
for(;;)
{
/* producir dato_p */
write(fildes[1], dato_p,
4*sizeof(int));
}
}
}
}
}
write
Proceso
hijo
Proceso
padre
read
SO
pipe
Flujo de datos
[email protected]
17
Ejecución de mandatos con pipes
/* programa que ejecuta el mandato “ls | wc” */
void main(void)
{
int fd[2];
pid_t pid;
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'');
}
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” */
}
[email protected]
18
Ejecución de mandatos con pipes
(II)
P ro c e s o
S T D IN
S TD O U T
p ip e ( fd )
P ro c e s o
S T D IN
S TD O U T
fd [0 ]
fd [1 ]
fo r k ( )
p ip e
P ro c e s o
S T D IN
S TD O U T
fd [0 ]
fd [1 ]
P ro c e s o h ijo
p ip e
P ro c e s o
S T D IN
S TD O U T
fd [0 ]
fd [1 ]
r e d ir e c c ió n
P ro c e s o h ijo
S T D IN
S T D IN
p ip e
S TD O U T
P ro c e s o
exec
S TD O U T
P ro c e s o h ijo
S T D IN
S T D IN
p ip e
S TD O U T
S TD O U T
ls
wc
[email protected]
19
FIFOS




Igual que los pipes
Mecanismo de comunicación y
sincronización con nombre
Misma máquina
Servicios

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()


mkfifo(char *name,mode_t mode);

Crea un FIFO con nombre name
Igual semántica que los pipes

Cierre de un FIFO mediante

Borrado de un FIFO mediante
close()
unlink()
[email protected]
20
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]
21
Semáforos POSIX

sem_init(sem_t *sem, int shared, int val);


int sem_destroy(sem_t *sem);


Borra un semáforo con nombre.
int sem_wait(sem_t *sem);


Cierra un semáforo con nombre.
int sem_unlink(char *name);


Abre (crea) un semáforo con nombre.
int sem_close(sem_t *sem);


Destruye un semáforo sin nombre
sem_t *sem_open(char *name,int flag,mode_t mode,int val);


Inicializa un semáforo sin nombre
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]
22
Productor-consumidor con semáforos
(buffer acotado y circular)
P ro d u c to r
C o n su m id o r
[email protected]
23
Productor-consumidor con
semáforos (II)
/* crear los procesos
ligeros */
pthread_create(&th1, NULL,
Productor, NULL);
pthread_create(&th2, NULL,
Consumidor, NULL);
#define MAX_BUFFER 1024
#define DATOS_A_PRODUCIR 100000
sem_t elementos; /* elementos buffer */
sem_t huecos; /* huecos buffer */
sem_t mutex; /* controla excl.mutua */
/* esperar su finalizacion
*/
pthread_join(th1, NULL);
pthread_join(th2, NULL);
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);
sem_destroy(&huecos);
sem_destroy(&elementos);
sem_destroy(&mutex);
exit(0);
}
[email protected]
24
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]
25
Productor-consumidor con
semáforos (IV)
void Consumidor(void)
{
int pos = 0;
int dato;
int i;
/* código del Consumidor */
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]
26
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 */
pthread_create(&th3, NULL,
Lector, NULL);
pthread_create(&th4, NULL,
Escritor, NULL);
void main(void)
{
pthread_t th1, th2, th3, th4;
pthread_join(th1,
pthread_join(th2,
pthread_join(th3,
pthread_join(th4,
sem_init(&mutex, 0, 1);
sem_init(&sem_lec, 0, 1);
pthread_create(&th1, NULL, Lector,
NULL);
pthread_create(&th2, NULL,
Escritor, NULL);
}
NULL);
NULL);
NULL);
NULL);
/*cerrar todos los semaforos*/
sem_destroy(&mutex);
sem_destroy(&sem_lec);
exit(0);
[email protected]
27
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]
28
Lectores-escritores con
semáforos (III)
void Escritor(void)
{
sem_wait(&mutex);
dato = dato + 2;
sem_post(&mutex);
/* código del escritor */
/* modificar el recurso */
pthread_exit(0);
}
[email protected]
29
Objetos de memoria compartida

Permiten crear un
segmento de memoria
compartido entre
procesos de la misma
máquina.
Declaración
independiente de
variables
P ro ceso A
P ro ceso B
Te xto
Te xto
D a tos
v a r2
D a tos
v a r1
*va r1= 2

2
P ila
[email protected]
S eg m e n to
d e m e m o ria
co m p artid a
P ila
30
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]
31
Servicios (II)


len especifica el número de bytes a proyectar.
prot el tipo de acceso (lectura, escritura o ejecución).


flags especifica información sobre el manejo de los datos
proyectados (compartidos, privado, etc.).




PROT_READ, PROT_WRITE, PROT_EXEC o PROT_NONE.
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]
32
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
espacio
de direcciones (mmap)
Utiliza la zona de memoria compartida
Desproyecta la zona de memoria compartida
Cierra y borra el objeto de memoria compartida
[email protected]
33
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]
34
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]
35
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]
36
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]
37
Código del consumidor
#define MAX_BUFFER
#define DATOS_A_PRODUCIR
1024
100000
/* tamaño del buffer */
/* 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]
38
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]
39
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]
40
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]
41
Secciones críticas con mutex
lock(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 ro c e s o
lig e r o A
P ro c e s o
lig e r o B
lo c k m u te x
lo c k m u te x
S e c c ió n
c rític a
o b tie n e
u n lo c k m u te x
m u te x
P ro c e s o lig e ro e je c u ta n d o
P ro c e s o lig e ro b lo q u e a d o
P u n to d e s in c ro n iz a c ió n
[email protected]
42
Variables condicionales



Variables de sincronización
asociadas a un mutex
Conveniente ejecutarlas entre
lock y unlock.
Dos operaciones atómicas:


P ro c e s o
lig e ro A
P ro c e s o
lig e ro B
lo c k
lo c k
w a it
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
A d q u ie re e l m u te x
u n lo c k m u te x
s ig n a l
S e co m p ite p o r e l m u te x
A d q u ie re e l m u te x
u n lo c k
P ro ce s o lige ro b lo q u ea d o e sp e ra n d o u n lo c k
P ro c e s o lig e ro b lo q u e a d o e s p e ra n d o s ig n a l
[email protected]
43
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]
44
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]
45
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]
46
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]
47
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]
48
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]
49
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]
50
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]
51
Lectores-escritores con mutex
(II)
pthread_create(&th1,
pthread_create(&th2,
pthread_create(&th3,
pthread_create(&th4,
pthread_join(th1,
pthread_join(th2,
pthread_join(th3,
pthread_join(th4,
NULL,
NULL,
NULL,
NULL,
Lector, NULL);
Escritor, NULL);
Lector, NULL);
Escritor, NULL);
NULL);
NULL);
NULL);
NULL);
pthread_mutex_destroy(&mutex);
pthread_mutex_destroy(&mutex_lectores);
exit(0);
}
[email protected]
52
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]
53
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]
54
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)

Múltiples soluciones
Paso de mensajes en POSIX
(colas de mensajes):



Primitivas básicas:


send(destino, mensaje): envía un
mensaje al proceso destino
receive(origen, mensaje): recibe
un mensaje del proceso origen



[email protected]
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
55
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]
56
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]
57
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]
58
Secciones críticas con colas de
mensajes (II)
if (fork() == 0) { /* proceso
for(;;){
mq_receive(mutex, &c,
/* sección crítica */
mq_send(mutex, &c, 1,
}
}else { /* proceso padre */
for(;;){
mq_receive(mutex, &c,
/* sección crítica */
mq_send(mutex, &c, 1,
}
}
hijo */
1, NULL);/*entrada sec. crítica */
0);
/* salida sec. critica */
1, NULL);/*entrada sec. crítica */
0);
/* salida sec. critica */
}
[email protected]
59
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 almacenamiento
de 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]
60
Productor-consumidor con colas
de mensajes (II)
if (almacen == -1)
{
perror("mq_open");
exit(1);
}
if (fork() == 0)
Productor();
else
Consumidor();
/* proceso hijo */
/* proceso padre */
exit(0);
}
[email protected]
61
Productor-consumidor con colas
de mensajes (III)
void Consumidor(void){
int dato;
int i;
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;
}
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]
62
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]
63
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]
64
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]
65
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);
}
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);
pthread_mutex_lock(mutex);
while (n_lectores > 0)
n_lectores ++;
pthread_cond_wait(&no_lectores,
peticion.tipo = OK;
&mutex);
mq_send(cola_local, peticion,
sizeof(peticion), 0);
peticion.tipo = OK;
mq_receive(cola_local, peticion,
mq_send(cola_local, peticion,
sizeof(peticion), NULL);
sizeof(peticion), 0);
n_lectores --;
mq_receive(cola_local, peticion,
if (n_lectores == 0)
sizeof(peticion), NULL);
pthread_cond_signal(&no_lectores);
pthread_mutex_unlock(mutex);
pthread_mutex_unlock(mutex);
}
[email protected]
66
Servidor multithread con colas de
mensajes
P ro c e s o c lie n te
C o la d e l
c lie n te
P ro c e s o c lie n te
C o la d e l
c lie n te
p e tic ió n
p e tic ió n
P ro c e s o s e rv id o r
re s p u e s ta
C o la d e l
s e rv id o r
re s p u e s ta
C re a c ió n d e l
th re a d
C re a c ió n d e l
th re a d
T h re a d q u e
s irv e la p e tic ió n
T h re a d q u e
s irv e la p e tic ió n
[email protected]
67
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]
68
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]
69
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]
70
Ejemplo de código cliente
struct mensaje {/* estructura mensaje */
char buffer[1024]; /* datos envío */
char cliente[256]; /* cola cliente */
};
q_cliente = mq_open("CLIENTE",
O_CREAT|O_RDONLY, 0700, 0);
q_servidor = mq_open("SERVIDOR",
O_WRONLY);
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 */
/* preparar peticion */
mq_send(q_servidor, &peticion,
sizeof(struct mensaje), 0);
attr.mq_maxmsg = 1;
attr.mq_msgsize =
sizeof(struct mensaje);
/* 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]
71
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]
72
Sockets (II)
zipi
138.100.240.2
patan
138.100.240.36
ftp
21
Red de interconexión
telnet
23
batman
138.100.240.32
merlin
138.100.240.50
[email protected]
conan
138.100.240.20
73
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:


FIFOS:






Si pipe vacío  read() se
bloquea
Si pipe lleno  write() se
bloquea
[email protected]
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
74
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:



Colas de mensajes POSIX:





Mecanismo de
sincronización adecuado
para procesos ligeros
Mecanismo de nombrado:
sin nombre
Identificación: variables
[email protected]

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
75
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]
76
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
s_espera(S)
s_espera(Q)
.
s_abre(S)
s_abre(Q)
P1
s_espera(Q)
s_espera(S)
.
s_abre(Q)
s_abre(S)

Ejemplo: Si C1 y C2 son dos
colas de mensajes
P0
receive(C1, M)
.
send(C2, M)

P1
receive(C2, N)
.
send(C1, N)
Condiciones del interbloqueo:




[email protected]
Exclusión mutua
Retención y espera
No apropiación
Espera circular
77
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)
P
A
B
Q

Interbloqueo
[email protected]
78
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
[email protected]
79
Descargar

Comunicación y sincronización entre procesos