prob sincro

25
1 PROBLEMAS DE SO2 mayo 2003 PROBLEMA 1 Tenemos un puente levadizo sobre un río con las siguientes condiciones de utilización: Los barcos tienen siempre prioridad de paso, pero para levantar el puente han de esperar a que no haya ningún coche sobre él. Los coches pueden utilizar el puente si no hay ningún barco pasando (en cuyo caso el puente estará levantado) o esperando. Escribir los algoritmos para barcos y coches utilizando semáforos y monitores. Solución con semáforos: #include <pthread.h> #include <semaphore.h> #define NUM_COCHES 5 #define NUM_BARCOS 5 void bajar_puente(void) {} void pasar(void) {} void levantar_puente(void) {} /*-----------------------------------------------------*/ /* Variables compartidas. */ /*-----------------------------------------------------*/ sem_t turno; /* Necesario para dar prioridad */ /* a los barcos. */ sem_t mutex1; /* Exclusión mutua entre coches.*/ sem_t mutex2; /* Exclusión mutua entre barcos.*/ sem_t libre; /* Indica si el puente está */ /* libre. */ int barcos = 0; /* Número de barcos intentando */ /* pasar. */ int coches = 0; /* Número de coches intentando */ /* pasar. */ /*-----------------------------------------------------*/ /* Código de los coches. */ /*-----------------------------------------------------*/ void *coche(void *arg) { sem_wait(&turno); /* Verificar si podemos pasar. */ sem_post(&turno); sem_wait(&mutex1); /* Exclusión mutua en el acceso */ /* a la variable coches. */ coches++; /* Incrementar el nº de coches. */ if (coches==1) /* Bloquear paso a los barcos, o*/ sem_wait(&libre); /* a los siguientes coches, si */ /* ya hubiese barcos. */ sem_post(&mutex1); /* Fin de la S.C. sobre coches. */ bajar_puente(); /* Funciones ya definidas. */ pasar(); sem_wait(&mutex1); /* S.C. para acceder a coches. */ coches--; /* Decrementar nº de coches. */ if (coches==0) /* Si es el último, libera el */ sem_post(&libre); /* paso a los barcos. */ sem_post(&mutex1); /* Fin de la S.C. sobre coches. */ } /*-----------------------------------------------------*/ /* Código de los barcos. */ /*-----------------------------------------------------*/ void *barco(void *arg) {

Upload: ignacio-ambiado-makray

Post on 27-Dec-2015

73 views

Category:

Documents


0 download

TRANSCRIPT

1

PROBLEMAS DE SO2 mayo 2003

PROBLEMA 1 Tenemos un puente levadizo sobre un río con las siguientes condiciones de utilización:

• Los barcos tienen siempre prioridad de paso, pero para levantar el puente han de esperar a que no haya ningún coche sobre él.

• Los coches pueden utilizar el puente si no hay ningún barco pasando (en cuyo

caso el puente estará levantado) o esperando.

Escribir los algoritmos para barcos y coches utilizando semáforos y monitores. Solución con semáforos: #include <pthread.h> #include <semaphore.h> #define NUM_COCHES 5 #define NUM_BARCOS 5 void bajar_puente(void) {} void pasar(void) {} void levantar_puente(void) {} /*-----------------------------------------------------*/ /* Variables compartidas. */ /*-----------------------------------------------------*/ sem_t turno; /* Necesario para dar prioridad */ /* a los barcos. */ sem_t mutex1; /* Exclusión mutua entre coches.*/ sem_t mutex2; /* Exclusión mutua entre barcos.*/ sem_t libre; /* Indica si el puente está */ /* libre. */ int barcos = 0; /* Número de barcos intentando */ /* pasar. */ int coches = 0; /* Número de coches intentando */ /* pasar. */ /*-----------------------------------------------------*/ /* Código de los coches. */ /*-----------------------------------------------------*/ void *coche(void *arg) { sem_wait(&turno); /* Verificar si podemos pasar. */ sem_post(&turno); sem_wait(&mutex1); /* Exclusión mutua en el acceso */ /* a la variable coches. */ coches++; /* Incrementar el nº de coches. */ if (coches==1) /* Bloquear paso a los barcos, o*/ sem_wait(&libre); /* a los siguientes coches, si */ /* ya hubiese barcos. */ sem_post(&mutex1); /* Fin de la S.C. sobre coches. */ bajar_puente(); /* Funciones ya definidas. */ pasar(); sem_wait(&mutex1); /* S.C. para acceder a coches. */ coches--; /* Decrementar nº de coches. */ if (coches==0) /* Si es el último, libera el */ sem_post(&libre); /* paso a los barcos. */ sem_post(&mutex1); /* Fin de la S.C. sobre coches. */ } /*-----------------------------------------------------*/ /* Código de los barcos. */ /*-----------------------------------------------------*/ void *barco(void *arg) {

2

sem_wait(&mutex2); /* S.C. para acceder a "barcos".*/ barcos++; /* Incrementar nº de barcos. */ if (barcos==1) { /* Si es el primero, bloquea el */ sem_wait(&turno); /* paso a los coches, o al resto*/ sem_wait(&libre); /* de barcos, mientras haya co- */ } /* ches. */ sem_post(&mutex2); /* Fin de la S.C. sobre barcos. */ levantar_puente(); pasar(); sem_wait(&mutex2); /* S.C. para acceder a barcos. */ barcos--; /* Decrementar nº de barcos. */ if (barcos==0) { /* Si es el último, libera el */ sem_post(&turno); /* paso a los coches. */ sem_post(&libre); } sem_post(&mutex2); /* Fin de la S.C. sobre barcos. */ } int main(void) { /* Identificadores de los hilos.*/ pthread_t id_barcos[NUM_BARCOS]; pthread_t id_coches[NUM_COCHES]; int i,j; /* Contador para la creación de */ /* hilos. */ /* Inicializar todos los semá- */ /* foros como no compartidos, y */ /* con valor 1. */ sem_init(&turno, 0, 1); sem_init(&mutex1, 0, 1); sem_init(&mutex2, 0, 1); sem_init(&libre, 0, 1); /* Crear los hilos. */ i=NUM_BARCOS; j=NUM_COCHES; while (i>0 || j>0) { if (i>0) { pthread_create( &id_barcos[i], NULL, barco, NULL ); i--; } if (j>0) { pthread_create( &id_coches[j], NULL, coche, NULL ); j--; } } /* Esperar su terminación. */ for (i=0; i<NUM_BARCOS; i++) pthread_join( id_barcos[i], NULL ); for (j=0; j<NUM_COCHES; j++) pthread_join( id_coches[j], NULL ); return 0; /* Terminar. */ }

Solución mediante monitores (pseudo-Pascal): TYPE puente_levadizo = MONITOR; VAR coches, barcos, barcos_bloqueados : integer; OkCoche, OkBarco : condition; PROCEDURE ENTRY entrar_coche; BEGIN (* Para dar prioridad a los barcos, los coches se suspenden si hay algún barco esperando. *) IF (barcos>0) OR (barcos_bloqueados>0) THEN OkCoche.wait; coches := coches + 1; OkCoche.signal; (* Liberamos en cascada al resto de coches, si los hay. *) bajar_puente END; PROCEDURE ENTRY salir_coche; BEGIN coches := coches – 1; (* Si ya no quedan coches, dejamos pasar a los barcos. *)

3

IF coches=0 THEN OkBarco.signal END; PROCEDURE ENTRY entrar_barco; BEGIN (* Si ya hay coches pasando, el barco se suspende. *) IF coches>0 THEN BEGIN barcos_bloqueados := barcos_bloqueados + 1; OkBarco.wait; barcos_bloqueados := barcos_bloqueados – 1 END; barcos := barcos + 1; (* Reactivamos al resto de barcos, en cascada. *) OkBarco.signal; levantar_puente END; PROCEDURE ENTRY salir_barco; BEGIN barcos := barcos – 1; (* Si es el último barco, deja pasar a los coches. *) IF barcos=0 THEN OkCoche.signal END; BEGIN (* Inicialización del monitor. *) barcos := 0; coches := 0; barcos_bloqueados := 0 END; Solución mediante monitores (implantación en POSIX): #include <pthread.h> /*-----------------------------------------------------*/ /* Variables globales. */ /*-----------------------------------------------------*/ pthread_mutex_t mutex_monitor; pthread_cond_t ok_barco; pthread_cond_t ok_coche; int coches = 0; int barcos = 0; int barcos_bloqueados = 0; /*-----------------------------------------------------*/ /* Procedimientos de entrada. */ /*-----------------------------------------------------*/ void entrar_coche(void) { pthread_mutex_lock(&mutex_monitor); /* Damos prioridad a los barcos.*/ /* Si alguno ha pedido entrar y */ /* está suspendido, bloqueará al*/ /* coche que intenta entrar. */ while ((barcos>0) || (barcos_bloqueados>0)) pthread_cond_wait(&ok_coche, &mutex_monitor); coches++; /* Es preferible utilizar broad-*/ /* cast en lugar de signal, pues*/ /* así garantizamos que se re- */ /* evalúe la condición. Con esta*/ /* instrucción reactivamos al */ /* resto de coches. */ pthread_cond_broadcast(&ok_coche); bajar_puente(); pthread_mutex_unlock(&mutex_monitor); } void salir_coche(void) { pthread_mutex_lock(&mutex_monitor); coches--; if (coches==0) pthread_cond_broadcast(&ok_barco); pthread_mutex_unlock(&mutex_monitor); } void entrar_barco(void) { pthread_mutex_lock(&mutex_monitor); while (coches > 0) { barcos_bloqueados++;

4

pthread_cond_wait(&ok_barco, &mutex_monitor); barcos_bloqueados--; } barcos++; pthread_cond_broadcast(&ok_barco); levantar_puente(); pthread_mutex_unlock(&mutex_monitor); } void salir_barco(void) { pthread_mutex_lock(&mutex_monitor); barcos--; if (barcos==0) pthread_cond_broadcast(&ok_coche); pthread_mutex_unlock(&mutex_monitor); } int main(void) { /* En el programa principal, en-*/ /* tre otras, habría que inicia-*/ /* lizar las herramientas de */ /* sincronización. */ pthread_mutex_init(&mutex_monitor, NULL); pthread_cond_init(&ok_barco, NULL); pthread_cond_init(&ok_coche, NULL); ... }

PROBLEMA 2 Disponemos de dos caminos que separan dos sentidos de circulación: Norte y Sur. Ambos caminos convergen en uno solo cuando se trata de cruzar un río. Existen coches que quieren pasar de norte a sur, y de sur a norte. En cualquier momento, sólo pueden atravesar el puente uno o más coches que vayan en el mismo sentido (no se mezclan coches que van en sentidos opuestos). Implementar una solución mediante semáforos. Solución #include <pthread.h> #include <semaphore.h> /*-----------------------------------------------------*/ /* Variables compartidas. */ /*-----------------------------------------------------*/ sem_t turno,libre; /* Necesarios para fijar el sen-*/ /* tido de paso. */ sem_t mutex1; /* Exclusión mutua en el acceso */ /* a la variable “norte”. */ sem_t mutex2; /* Ídem para la variable “sur”. */ int norte=0; /* Nº de vehículos en dirección */ /* norte-->sur. */ int sur=0; /* Nº de vehículos en dirección */ /* sur-->norte. */ /*-----------------------------------------------------*/ /* Funciones utilizadas en los hilos. */ /*-----------------------------------------------------*/ void pasar_puente(void) {} /*-----------------------------------------------------*/ /* Hilo que representa a un vehículo en sentido N-->S */ /*-----------------------------------------------------*/ void *norte_sur(void *arg) { sem_wait(&turno); /* Exclusión mutua a la hora de */ /* pedir el paso. */ sem_wait(&mutex1); /* S.C. para acceder a “norte”. */ norte++; if (norte==1) /* El primer vehículo del norte */ sem_wait(&libre); /* no deja pasar a los del sur. */ sem_post(&mutex1); /* Fin de la S.C. de “norte”. */ sem_post(&turno); /* Otro hilo podrá pedir paso. */

5

pasar_puente(); /* Proceder a cruzar. */ sem_wait(&mutex1); /* S.C. para acceder a “norte”. */ norte--; if (norte==0) /* Si es el último, permite que */ sem_post(&libre); /* los del sur pasen. */ sem_post(&mutex1); /* Fin de la S.C. de “norte”. */ } /*-----------------------------------------------------*/ /* Hilo que representa a un vehículo en sentido S-->N */ /*-----------------------------------------------------*/ void *sur_norte(void *arg) { sem_wait(&turno); /* Exclusión mutua a la hora de */ /* pedir el paso. */ sem_wait(&mutex2); /* S.C. para acceder a “sur”. */ sur++; if (sur==1) /* El primer vehículo del sur no*/ sem_wait(&libre); /* deja pasar a los del norte. */ sem_post(&mutex2); /* Fin de la S.C. de “sur”. */ sem_post(&turno) ; /* Otro hilo podrá pedir paso. */ pasar_puente(); /* Proceder a cruzar. */ sem_wait(&mutex2); /* S.C. para acceder a “sur”. */ sur--; if (sur==0) /* Si es el último, permite que */ sem_post(&libre); /* los del norte pasen. */ sem_post(&mutex2); /* Fin de la S.C. de “sur”. */ } /*-----------------------------------------------------*/ /* En el programa principal (no mostrado) tendríamos */ /* estas instrucciones de inicialización: */ /* sem_init(&turno, 0, 1); */ /* sem_init(&libre, 0, 1); */ /* sem_init(&mutex1, 0, 1); */ /* sem_init(&mutex2, 0, 1); */ /*-----------------------------------------------------*/

PROBLEMA 3 Una barbería dispone de un sillón para el corte de pelo, un barbero y una cantidad N de sillas en la sala de espera para los clientes. Si no hay clientes que atender, el barbero se echa una siesta. Cuando en este caso llega un cliente, tiene que despertar al barbero. Si llegan más clientes mientras el barbero atiende a alguien, pueden sentarse (si quedan sillas libres en la antesala) o irse a pasear (si no quedan sillas disponibles). Mientras el barbero atiende a un cliente, este último espera detenido hasta que finalice el corte de pelo. El problema consiste en diseñar un programa adecuado para que se logre la sincronización adecuada. Debe resolverse implementando en POSIX el equivalente a un monitor, respetando la interfaz que se presenta en el ejemplo de uso siguiente: #include <pthread.h> typedef enum {LLENO, CORTADO} respuesta; /*-----------------------------------------------------*/ /* Código del hilo que representa a un cliente. */ /*-----------------------------------------------------*/ void *cliente(void *arg) { respuesta res; do { /* Operación del monitor. */ entrar_barberia(&res); if (res==LLENO) dar_una_vuelta(); } while (res!=CORTADO);

6

} /*-----------------------------------------------------*/ /* Código del hilo que representa al barbero. */ /*-----------------------------------------------------*/ void *barbero(void *arg) { while (1) { esperar_cliente(); /* Operación del monitor. */ cortar_pelo(); /* No pertenece al monitor, sólo*/ /* simula el corte. */ fin_corte(); /* Operación del monitor. */ } } int main(void) { int i; pthread_t id_clientes[NUM_CLIENTES]; pthread_t id_barbero; /* Crear clientes y barbero. */ pthread_create(&id_barbero, NULL, barbero, NULL); for (i=0; i<NUM_CLIENTES; i++) pthread_create(&id_clientes[i], NULL, cliente, NULL); /* Esperar a los clientes. */ for (i=0; i<NUM_CLIENTES; i++) pthread_join(id_clientes[i], NULL); return 0; /* Terminar. */ }

Solución El código que aparece a continuación formaría parte del programa mostrado en el enunciado. /*-----------------------------------------------------*/ /* Variables globales. */ /*-----------------------------------------------------*/ /* Para exclusión mutua en las */ /* operaciones del monitor. */ pthread_mutext_t m_monitor = PTHREAD_MUTEX_INITIALIZER; int n_clientes = 0; /* Número de clientes en la bar-*/ /* bería. */ /* Sillas de espera para los */ /* clientes (basta con una con- */ /* dición). */ pthread_cond_t silla = PTHREAD_COND_INITIALIZER; /* Sillón para el corte de pelo.*/ pthread_cond_t sillon = PTHREAD_COND_INITIALIZER; /* Litera del barbero. */ pthread_cond_t dormir = PTHREAD_COND_INITIALIZER; /*-----------------------------------------------------*/ /* Operaciones del monitor. */ /*-----------------------------------------------------*/ void entrar_barberia(respuesta *resp) { pthread_mutex_lock( &m_monitor ); if (n_clientes > N) *resp = LLENO; /* No cabe, que lo vuelva a in- */ /* tentar. */ else { n_clientes++; if (n_clientes==1) /* Despertar al barbero. */ pthread_cond_signal( &dormir ); /* Si no somos el primero, habrá*/

7

/* que esperar. */ else pthread_cond_wait( &silla, &m_monitor ); /* Esperar a que acabe el corte.*/ pthread_cond_wait( &sillon, &m_monitor ); *resp = CORTADO; } pthread_mutex_unlock( &m_monitor ); } void esperar_cliente(void) { pthread_mutex_lock( &m_monitor ); if (n_clientes == 0) /* Echar una siesta si no hay */ /* clientes. */ pthread_cond_wait( &dormir, &m_monitor ); else /* Sino, llamar al primero. */ pthread_cond_signal( &silla ); pthread_mutex_unlock( &m_monitor); } void fin_corte(void) { pthread_mutex_lock( &m_monitor ); /* Decirle al cliente que baje */ /* del sillón y salga de la bar-*/ /* bería (cuando quiera). */ pthread_cond_signal( &sillon ); n_clientes--; pthread_mutex_unlock( &m_monitor ); }

8

CUESTIONES RESUELTAS

Q1) Indique para cada una de las siguientes afirmaciones si es verdadera (V) o falsa (F).

a) Un proceso puede suspenderse al realizar una operación V sobre un semáforo.

b) Un proceso siempre se suspende al realizar una operación P sobre un semáforo.

c) Un proceso siempre se suspende al realizar una operación espera (wait) sobre una variable condición.

d) Un proceso siempre se suspende al realizar una operación señal (signal) sobre una variable condición.

e) Un proceso siempre despierta a otro proceso al realizar una operación V sobre un semáforo.

f) Un proceso siempre despierta a otro proceso al realizar una operación señal (signal) sobre una variable condición.

Solución Son FALSAS las afirmaciones a), b), d) y f) Son VERDADERAS las afirmaciones c) y e)

Q2) ¿Qué imprime por la salida estándar el siguiente programa?

#include <pthread.h> pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER; void proc1(char *arg){ pthread_mutex_lock(&mutex); printf(arg); pthread_mutex_unlock(&mutex); if (strcmp(arg,”2”)) pthread_exit(0); }

main (){ pthread_t id1,id2,id3; pthread_mutex_lock(&mutex); pthread_create(&id1,NULL,proc1,"S"); pthread_create(&id2,NULL,proc1,"O"); pthread_create(&id3,NULL,proc1,"1"); proc1("2"); pthread_mutex_unlock(&mutex); exit(0); }

Solución No imprime nada. Todos los hilos, incluyendo al principal, se suspenden en la operación pthread_mutex_lock(&mutex) de la función proc1 al estar mutex cerrado y adquirido por el hilo principal. Q3) Sea el siguiente programa en C y POSIX.

9

#include <semaphore.h> #include <pthread.h> #include <stdlib.h> #include <stdio.h> sem_t s,t; int x ; void *hilo1(void *arg) { int i,j; char c; c=getchar(); x=atoi(c);/*convierte a entero*/ j=5; for (i=1; i<=10; i++) { j = j + x; x = x + 1; } printf(“j:%d\n”, j ); }

void *hilo2(void *arg) { int k, j; k = x * 2; for(j=1; j<=3; j++) { k = k + j*x; x = x - 1; } printf( “k:%d \n“, k ); } int main(void) { pthread_t h1,h2; x = 0; sem_init(&s,0,0); /*Inicializa a 0*/ sem_init(&t,0,1); /*Inicializa a 1*/ pthread_create(&h1,NULL,hilo1,NULL); pthread_create(&h2,NULL,hilo2,NULL); pthread_join(h1,NULL); pthread_join(h2,NULL); }

Se pide completar el código de los procedimientos hilo1 e hilo2, utilizando los semáforos declarados como variables globales para que los accesos sobre la variable global x se realicen en exclusión mutua y el valor que ha de imprimir el procedimiento hilo2 siempre aparezca en pantalla tras haberse escrito el del hilo1. Solución

#include <semaphore.h> #include <pthread.h> #include <stdlib.h> #include <stdio.h> sem_t s,t; int x ; void *hilo1(void *arg) { int i,j; char c; sem_wait(&t); c=getchar(); x=atoi(c); j=5; for (i=1; i<=10; i++) { j = j + x; x = x + 1; } sem_post(&t); printf(“j:%d\n”, j ); sem_post(&s); }

void *hilo2(void *arg) { int k, j; sem_wait(&t); k = x * 2; for(j=1; j<=3; j++) { k = k + j*x; x = x - 1; } sem_post(&t); sem_wait(&s); printf( “k:%d \n“, k ); } int main(void) { pthread_t h1,h2 ; x = 0; sem_init(&s,0,0); /*Inicializa a 0*/ sem_init (&t,0,1); /Inicializa a 1*/ pthread_create(&h1,NULL,hilo1,NULL); pthread_create(&h2,NULL,hilo2,NULL); pthread_join(h1,NULL); pthread_join(h2,NULL); }

10

Q4) Suponga que un programador está implementando el módulo de semáforos en un sistema operativo y por error ha codificado las operaciones P y V de la forma siguiente:

struct semaforo{ int contador; PCB cola; } void P(semaforo *s) { int pidproc; s->contador++; if (s->contador < 0) { pidproc = getpid(); InsetarEnCola(s->cola,pidproc); Reanudar(pidproc); } }

void V(semaforo *s) {int pidproc; s->contador++; if (s->contador <= 0) { ExtraerDeCola(s->cola,pidproc); Suspender(pidproc); } }

Indique cuál es el error que ha cometido.

Solución Reanudar(p) debe estar en lugar de Suspender(p) y viceversa

Q5) Comente qué valores posibles tendrían las variables x e y al finalizar la ejecución de los siguientes tres procesos concurrentes. Los valores iniciales son los siguientes: x=1, y=4, S1=1, S2=0 y S3=1.

Proceso A P(S2); P(S3); x = y * 2; y = y + 1; V(S3);

Proceso B P(S1); P(S3); x = x + 1; y = 8 + x; V(S2); V(S3);

Proceso C P(S1); P(S3); x = y + 2; y = x * 4; V(S3); V(S1);

Solución Existen dos posibles soluciones que vienen dadas por las siguientes secuencias de ejecución: 1) x = x + 1; y = 8 + x; x = y * 2; y = y + 1; ⇒ x = 20, y = 11 2) x = y + 2; y = x * 4; x = x + 1; y = 8 + x; x = y * 2; y = y + 1; ⇒ x = 30, y = 16

Q6) Sea un proceso que crea tres tipos de hilos que ejecutan las funciones Proceso1, 2 y 3, respectivamente. Cada uno de ellos ejecuta una sección crítica SC1, SC2 y SC3 tal como se muestra en el código de las dos tablas. Nótese que existen muchos procesos de los tres tipos 1, 2 y 3 y que todos ellos antes de acceder a su sección crítica ejecutan una función de entrada y cuando finalizan ejecutan una función de salida (comportamiento tipo monitor).

#include <semaphore.h> #include <pthread.h> #include <stdlib.h> #include <stdio.h> #define NUM_HILOS 20 void EntraA(); void EntraB(); void EntraC(); void SaleA(); void SaleB();

void *Proceso2() { while(1) { SeccionRestante2; EntraB(); SC2; SaleB(); } } void *Proceso3()

11

void SaleC(); pthread_mutex_t mutex; pthread_cond_t mutexA, mutexB, mutexC; int nA, nB, nC; void *Proceso1() { while(1) { SeccionRestante1; EntraA(); SC1; SaleA(); } }

{ while (1) {SeccionRestante3; EntraC(); SC3; SaleC(); } }

void EntraA() { pthread_mutex_lock(&mutex); while (nB>0) pthread_cond_wait(&mutexA, &mutex); nA=nA+1; pthread_mutex_unlock(&mutex); } void SaleA() { pthread_mutex_lock(&mutex); nA=nA-1; pthread_cond_signal(&mutexA); pthread_cond_signal(&mutexB); pthread_cond_signal(&mutexC); pthread_mutex_unlock(&mutex); } void EntraB() { pthread_mutex_lock(&mutex); while((nA>0)||(nC>0)) pthread_cond_wait(&mutexB,&mutex); mutexB.wait; nB=nB+1; pthread_mutex_unlock(&mutex); } void SaleB() { pthread_mutex_lock(&mutex); nB=nB-1; pthread_cond_signal(&mutexA); pthread_cond_signal(&mutexB); pthread_cond_signal(&mutexC); pthread_mutex_unlock(&mutex); }

void EntraC() { pthread_mutex_lock(&mutex); while ((nC>0) || (nB>0)) pthread_cond_wait(&mutexC, &mutex); nC=nC+1; pthread_mutex_unlock(&mutex); } void SaleC() { pthread_mutex_lock(&mutex); nC=nC-1; pthread_cond_signal(&mutexA); pthread_cond_signal(&mutexB); pthread_cond_signal(&mutexC); pthread_mutex_unlock(&mutex); } int main() { int i; pthread_t P1,P2,P3; nA:=0;nB:=0;nC:=0; pthread_mutex_init(&mutez,NULL); pthread_cond_init(&mutexA,NULL); pthread_cond_init(&mutexB,NULL); pthread_cond_init(&mutexC,NULL); for(i=1;i<NUM_HILOS; i++) {pthread_create(&P1,NULL,Proceso1,NULL); pthread_create(&P2,NULL,Proceso2,NULL); pthread_create(&P3,NULL,Proceso3,NULL); } for(i=1;i<NUM_HILOS; i++) {pthread_join(&P1,NULL); pthread_join(&P2,NULL); pthread_join(&P3,NULL); } }

a) Indique qué tipo de sincronización proporciona el monitor anterior. Para ello,

conteste en la siguiente tabla en la cual debe colocar una X en el elemento (i,j) cuando la sección crítica de un proceso de tipo i se pueda ejecutar concurrentemente con la sección crítica de un proceso de tipo j.

b) Modifique los procedimientos Proceso1, Proceso2 y Proceso3 (dejando intacta la implementación del las funciones EntraA, EntraB, EntraC, SaleA, SaleB, SaleC ) para conseguir el tipo de sincronización que se muestra en la siguiente tabla.

12

SC1 SC2 SC3 SC1 X X --- SC2 X --- --- SC3 --- --- X

Solución a)

SC1 SC2 SC3 SC1 X -- X SC2 -- X -- SC3 X -- --

b) #include <semaphore.h> #include <pthread.h> #include <stdlib.h> #include <stdio.h> #define NUM_HILOS 20 void EntraA(); void EntraB(); void EntraC(); void SaleA(); void SaleB(); void SaleC(); pthread_mutex_t mutex; pthread_cond_t mutexA, mutexB, mutexC; int nA, nB, nC; void *Proceso1() { while(1) { SeccionRestante1; EntraA(); SC1; SaleA(); } }

void *Proceso2() { while(1) { SeccionRestante2; EntraC(); SC2; SaleC(); } } void *Proceso3() { while (1) {SeccionRestante3; EntraB(); SC3; SaleB(); } }

Q7)¿Qué llamadas al sistema utilizaría para implementar la orden cp de UNIX ?.

Solución Fundamentalmente, open, read, write y close.

Q8) Cite tres atributos que se hereden de procesos padres a procesos hijos en UNIX.

Solución La imagen de memoria, el UID, el GID, la tabla de descriptores de ficheros, etc.

Q9)¿Cual es el mecanismo para eliminar o interrumpir la ejecución de procesos en UNIX?. Ponga un ejemplo.

Solución Las señales. Su acción por defecto es matar, aunque se les puede asociar un manejador. Ejemplos de señales: Pulsar la tecla CTRL-C, una división por cero, cualquiera de las generadas con la orden kill.

Q10) ¿Qué tipos de ficheros existen en UNIX?

Solución Regulares, directorios y especiales. Estos últimos representan los dispositivos de E/S. También los tubos y otros mecanismos de comunicación entre procesos son pseudoficheros.

13

Q11) ¿A qué ficheros están asociados los descriptores de ficheros 0, 1 y 2 de un proceso UNIX que se invoca de la siguiente forma:

$ mail pepe < carta 2> f Solución

Descriptor 0 carta Descriptor 1 /dev/tty (la salida estándar que herede del shell) Descriptor 2 f

Q12) Resuelva el problema de exclusión mutua de una sección crítica utilizando la operación test-and-set atómica.

Solución int cerrojo=0; (* inicialmente FALSE *) … while test_and_set(&cerrojo) ; SECCION CRITICA cerrojo = 0; …

Q13) ¿Cuales son los posibles valores que tomará x como resultado de la ejecución concurrente de los siguientes hilos?

#include <semaphore.h> #include <pthread.h> #include <stdlib.h> #include <stdio.h> sem_t s1,s2,s3; int x; void *func_hilo1(void *a) { sem_wait(&s1); sem_wait(&s2); x=x+1; sem_post(&s3); sem_post(&s1); sem_post(&s2); } void *func_hilo2(void *b) { sem_wait(&s2); sem_wait(&s1); sem_wait(&s3); x=10*x; sem_post(&s2); sem_post(&s1); }

int main() { pthread_t h1,h2 ; x = 1; sem_init(&s1,0,1); /*Inicializa a 1*/ sem_init(&s2,0,1); /*Inicializa a 1*/ sem_init(&s3,0,0); /*Inicializa a 0*/ pthread_create(&h1,NULL,func_hilo1,NULL); pthread_create(&h2,NULL,func_hilo2,NULL); pthread_join(h1,NULL); pthread_join(h2,NULL); }

Solución De la ejecución del código anterior se obtiene dos posibles valores de x, x=20 y x=1. En el segundo resultado ocurre un interbloqueo. El semáforo s3, inicializado a 0, obliga a que la sentencia x= 10 * x se ejecute después de la sentencia x = x+1. Si se ejecuta func_hilo1 (sin interrupción) y, a continuación, se ejecuta func_hilo2, el resultado es 20. Si el hilo h1 ejecuta sem_wait(&s1) de func_hilo1 y otro hilo h2 ejecuta

14

sem_wait(&s2) de func_hilo2 antes que h1 ejecute sem_wait(&s2) de func_hilo1 entonces se tiene un interbloqueo y el resultado es x=1. Q14) El código que se proporciona a continuación constituye una solución al problema de un puente levadizo.

void entrar_coche(void) { sem_wait(&turno); sem_wait(&mutex1); c = c +1; if (c==1) sem_wait(&libre); sem_post(&mutex1); sem_post(&turno); bajar_puente(); entrar_puente(); } /* fin entrar_coche*/ void salir_coche(void) { salir_puente; sem_wait(&mutex1); c = c-1; if (c==0) sem_post(&libre); sem_post(&mutex1); } /*fin salir_coche*/

void entrar_barco(void) { sem_wait(&mutex2); b = b+1; if (b==1) { sem_wait(&turno); sem_wait(&libre); } sem_post(&mutex2); levantar_puente(); entrar_puente(); } /* fin entrar_barco*/ void salir_barco(void) { salir_puente; sem_wait(&mutex2); b = b-1; if (b==0){ sem_post(&turno); sem_post(&libre); } sem_post(&mutex2); } /*fin salir_barco*/

Las condiciones de corrección mínimas que satisface esta solución son: • Los barcos pueden cruzar el puente cuando esté levantado. Para levantarlo no debe

haber ningún coche cruzando. • Los coches pueden cruzar el puente cuando esté bajado. Para bajarlo no debe haber

ningún barco cruzando. Inicialmente, todos los semáforos valen 1 y los contadores valen 0. Conteste a las siguientes preguntas suponiendo que los semáforos tienen asociada una cola FIFO.

a) Indique el estado de las colas asociadas a los semáforos y los procesos que están utilizando el puente después de invocar las siguientes operaciones (se considerará que el instante final de la operación es cuando ésta acaba o cuando el proceso que la invoca se suspende).

• C0 invoca entrar_coche. • B0 invoca entrar_barco. • C1 invoca entrar_coche. • B1 invoca entrar_barco.

b) Indique el estado de las colas asociadas a los semáforos y los procesos que están utilizando el puente después de invocar las siguientes operaciones:

• Todos los coches / barcos que han entrado en el puente en el apartado anterior han invocado salir_coche/ salir barco.

• C2 invoca entrar_coche. • B2 invoca entrar_barco. • C3 invoca entrar_coche.

c) ¿Qué ocurre si, hay coches en el puente, hay barcos esperando y un nuevo coche invoca entrar_coche?. d) ¿Qué ocurre si hay barcos cruzando bajo el puente, hay coches esperando y un nuevo barco invoca entrar_barco? e) ¿Qué ocurre si hay coches y barcos esperando y el último barco bajo el puente invoca salir_puente?

15

f) Como modificaría el código para que los coches pasaran de uno en uno (no necesita reescribirlo todo).

Solución a) C0 pasa, B0 se detiene en libre pero coge el turno, C1 se bloquea en turno que ha cogido B0, B1 no puede entrar porque B0 no ha liberado mutex2. Es decir:

variable contador Procesos suspendidos mutex1 1 mutex2 -1 B1turno -1 C1libre -1 B0En el puente se encuentra el proceso C0

b) Al salir C0 da paso en primer lugar a B0 al realizar P(libre), B1 pasa al liberar B0 mutex2, C2 llega y se suspende en turno, que aún no ha sido liberado por los barcos, B2 llega y pasa, C3 llega y se suspende en turno.

Variable contador Procesos suspendidos mutex1 1 mutex2 1 turno -3 C1, C2, C3libre 1 En el puente se encuentran B0, B1, B2

c) El coche se suspende en el semáforo turno. d) El nuevo barco cruza bajo el puente. e) Situación imposible. f) En entrar_coche se suprime la llamada sem_post(&mutex1) y en salir_coche se suprime la llamada sem_post(&mutex1). Al no liberar el semáforo mutex1 hasta después de salir del puente, si llega un nuevo coche y tiene el turno, se suspenderá en mutex1.

Q15) Dado el siguiente código que se comporta como un monitor:

#include <semaphore.h> #include <pthread.h> #include <stdlib.h> #include <stdio.h> pthread_mutex_t mutex; pthread_cond_t cola; int cerrada; void pasar(void) { pthread_mutex_lock(&mutex); if (cerrada) pthread_cond_wait(&cola,&mutex); pthread_cond_signal(&cola); pthread_mutex_unlock(&mutex); }/*fin pasar*/ void abrir(void) { pthread_mutex_lock(&mutex); if (cerrada) pthread_cond_signal(&cola); cerrada = 0; pthread_mutex_unlock(&mutex); }

void cerrar(void) { pthread_mutex_lock(&mutex); cerrada = 1; pthread_mutex_unlock(&mutex); }/*fin cerrar*/ int main(void) { cerrada = 1 pthread_mutex_init(&mutex,NULL); pthread_cond_init(&cola,NULL); : : } /*fin main*/

16

Conteste a las siguientes preguntas:

a) ¿Qué efecto tendrá una llamada a pasar si no se ha llamado todavía a abrir?

b) ¿Qué efecto tendrá una llamada a abrir? c) ¿En que diferiría el comportamiento de este código si no estuviese la

instrucción pthread_cond_signal(&cola) en el procedimiento pasar?

Solución a) Suspender al proceso en la variable condición cola. b) Si la barrera estaba cerrada se abre y se activan todos los procesos suspendidos. c) En que sólo pasaría uno de los procesos suspendidos en cola cada vez que se abre la barrera.

Q16) Dado el siguiente monitor:

type barrera=monitor var cola: condition; cerrada: boolean; procedure entry pasar; begin if cerrada then cola.wait; cola.signal end; procedure entry abrir; begin if cerrada then cola.signal; cerrada := FALSE end;

procedure entry cerrar; begin cerrada := TRUE end; begin cerrada := TRUE end.

Conteste a las siguientes preguntas: a) ¿Qué efecto tendrá una llamada a barrera.pasar si no se ha llamado todavía a

barrera.abrir? b) ¿Qué efecto tendrá una llamada a barrera.abrir? c) ¿En que diferiría el comportamiento de este monitor si no estuviese la instrucción

cola.signal en el procedimiento pasar?

Solución a) Suspender al proceso en la variable condición “cola”. b) Si la barrera estaba cerrada, se abre, y se activan todos los procesos suspendidos. c) En que sólo pasaría uno de los procesos suspendidos en cola cada vez que se abra

la barrera. Q17) Sean cuatro hilos de ejecución H1, H2, H3 y H4, que deben iniciar la ejecución del código que se presenta seguidamente. Estos hilos están en la cola del planificador en ese mismo orden y se utiliza un algoritmo FCFS. Indique cuáles de ellos van a finalizar su ejecución y en qué orden si todos los semáforos presentados valen inicialmente cero y son compartidos por todos los hilos.

H1 H2 H3 H4 P(S); V(S); V(S2); P(S2); x = x+1; P(S); x = x+2; V(S2); P(S3); x = 4; V(S3); P(S); x = x–2; V(S3); x = x*3; V(S);

Solución

17

Primero en terminar: H3; segundo: H1. H2 queda suspendido en P(S), H4 queda suspendido en P(S).

Q18) En un sistema se encuentran en ejecución cinco procesos: P0, P1, P2, P3 y P4 que utilizan los recursos R0, R1, R2, R3, R4 y RC. Inicialmente la cantidad de recursos de cada tipo es la indicada en la siguiente tabla:

Recurso R0 R1 R2 R3 R4 RCCantidad 1 1 1 1 1 n

El perfil de ejecución de un proceso Pi es distinto para los procesos pares e impares y es el indicado en la tabla siguiente:

Perfil de los procesos pares Perfil de los procesos impares while TRUE do Peticion(RC); Peticion(Ri); Peticion(R((i+1) mod 5))); UsoDeLosRecursos(); Libera(Ri); Libera(R((i+1) mod 5)); Libera(RC); SeccionRestante(); end while;

while TRUE do Peticion(RC); Peticion(R((i+1) mod 5))); Peticion(Ri); UsoDeLosRecursos(); Libera(Ri); Libera(R((i+1) mod 5)); Libera(RC); SeccionRestante(); end while;

Nota: Cada petición de recursos solicita un solo ejemplar del recurso en concreto y bloquea al proceso solicitante si el recurso no está disponible. Suponiendo que n=5 ¿Es posible que en el sistema se produzca un interbloqueo? Razone la respuesta. En caso afirmativo describa un escenario.

Solución No se producirá, ya que los procesos no intentan coger todos ellos los mismos recursos en el mismo orden por lo que es imposible que se cumpla la condición de espera circular (La condición de retención y espera puede llegar a cumplirla el proceso 4, pues puede haber obtenido el recurso 4 y después quedarse esperando el recurso 0, previamente concedido al proceso 0).

Q19) Describa al menos dos de las diferencias que existen entre las órdenes internas y las órdenes externas.

Solución Se podrían haber dado, entre otras, las siguientes: • Las órdenes internas deben ser ejecutadas directamente por el shell. En las

externas ha de crearse un proceso que ejecute un programa diferente para realizar las acciones requeridas.

• Las órdenes internas modifican directamente algún atributo del proceso correspondiente al shell. Estos atributos serán heredados por las siguientes órdenes externas que ejecute el shell. Ejemplos: directorio actual de trabajo (cd), máscara de creación de ficheros (umask), ...

• El conjunto de órdenes internas está limitado tras haber implementado el shell. Por el contrario, siempre podrán añadirse órdenes externas.

Q20) En un sistema multiprogramado existen tres procesos P1, P2, P3 que comparten memoria y cuyas secciones críticas (exclusión mutua) vienen indicadas como Operaciones1, Operaciones2, Operaciones3, Operaciones4, Operaciones5. Dichas secciones críticas se han de ejecutar en el orden indicado por el número que acompaña a

18

su nombre y en exclusión mutua. Resuelva el problema utilizando semáforos, para ello indique el valor inicial que tendrán los semáforos utilizados, así como el lugar y la operación sobre el semáforo a realizar. NOTA: Las dos Operaciones3 pueden ser ejecutadas concurrentemente por P2 y P3.

P1 P2 P3

Operaciones1;

Operaciones3;

Operaciones3;

Operaciones4;

Operaciones2; Operaciones5;

Solución Valores iniciales: Todos los semáforos a cero.

P1 P2 P3 P(S1); P(S1);

Operaciones1;

Operaciones3; P(S2);

Operaciones3; V(S2);

Operaciones4; P(S3); Operaciones2; V(S3); Operaciones5; V(S1); V(S1);

Q21) Analice el siguiente código y responda a la cuestión que aparece tras él: int contador; pthread_mutex_t mutex =

PTHREAD_MUTEX_INITIALIZER; pthread_cond_t negativo =

PTHREAD_COND_INITIALIZER; void *codigoA() { int i; for (i=0; i<100; i++) {

pthread_mutex_lock(&mutex); while (contador <=0) pthread_cond_wait(&negativo, &mutex); contador = contador+1; pthread_mutex_unlock(&mutex);

} pthread_exit(0); }

void *codigoB() { int i; for (i=0; i<100; i++) { pthread_mutex_lock(&mutex); contador = contador-1; pthread_cond_signal(&negativo); pthread_mutex_unlock(&mutex); } pthread_exit(0); } main( ) { pthread_t th_a, th_b; contador = 0; pthread_create(&th_a, NULL, codigoA, NULL); pthread_create(&th_b, NULL, codigoB, NULL); pthread_join(th_a, NULL); pthread_join(th_b, NULL); exit(0); }

Cuando la ejecución de todos los hilos no pueda avanzar más, indique el valor final de la variable contador y el estado de cada hilo.

Solución El valor del contador será -100. El hilo A estará suspendido en la condición “negativo”, el hilo B habrá terminado y el hilo principal estará suspendido en su primer pthread_join().

Q22) Se tienen tres procesos P1, P2 y P3 que desean ejecutar los procedimientos que

19

aparecen en los listados siguientes. En estos procedimientos, todos aquellos que empiezan con la letra “A” podrán ser ejecutados concurrentemente por dos procesos como máximo, mientras que los que empiecen con “B” deben ejecutarse en exclusión mutua (sólo entre los que empiecen con “B”, no con los empezados con “A”) y en el orden que sugiere el dígito que sigue a dicha letra. Utilice semáforos para proteger adecuadamente estos procedimientos e indique el valor inicial que deberá tener cada semáforo.

P1 P2 P3

A1; B2; A4; B5;

B1; B4; A2;

B3; A3; B6;

Solución Las instrucciones a utilizar aparecen en la tabla siguiente:

P1 P2 P3 A1; P(S1); B2; V(S2); P(M); A4; V(M); P(S4); B5; V(S5);

B1; V(S1); P(S3); B4; V(S4); P(M); A2; V(M);

P(S2); B3; V(S3); P(M); A3; V(M); P(S5); B6;

El valor inicial de todos los semáforos ha de ser cero, excepto en el semáforo M, que tendrá que valer 2. Existen soluciones con menos semáforos. Por ejemplo, donde se haya usado S4 se habría podido usar de nuevo S1 y donde se usó S5 se podría haber usado S2.

Q23) Razone por qué al crear un fichero en un sistema POSIX con la llamada open(“fichero”, O_CREAT | O_TRUNC | O_WRONLY, 0666); puede darse el caso de que dicho fichero tenga una palabra de protección “rw-r--r--”, pero no una “rwx--xr--” (por ejemplo).

Solución Porque todo proceso POSIX mantiene un atributo (llamado “máscara de creación de ficheros”) que indica qué derechos no deben otorgarse a los ficheros de nueva creación (sean del tipo que sean: regulares, directorios, etc.). Este atributo puede consultarse con la orden “umask” sin argumentos y puede modificarse facilitando un argumento cuando se emplee esa misma orden. En el ejemplo planteado, la primera palabra de protección tiene menos derechos que los especificados como segundo argumento del creat() (el valor 0666 octal implica la palabra de protección rw-rw-rw), cosa válida al emplear una máscara 022, pero la segunda palabra tiene derechos de ejecución para el propietario y el grupo y esos derechos no habían sido solicitados al crear el fichero. Por tanto, ningún valor de la máscara podría haber dado como resultado tal palabra de protección.

20

Q24) Dado el siguiente programa escrito en C con primitivas de sincronización POSIX, donde se asume que la función printf escribe directamente en memoria de vídeo y, por tanto, no llega a implicar la suspensión de los procesos (ya que no realiza una E/S que necesite espera):

#include <pthread.h> #include <stdio.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;pthread_cond_t cond = PTHREAD_COND_INITIALIZER; pthread_cond_t seg = PTHREAD_COND_INITIALIZER; pthread_cond_t ter = PTHREAD_COND_INITIALIZER; void *hilo1() { pthread_mutex_lock(&mutex); printf( “Hilo 1 espera.\n” ); pthread_cond_wait(&cond,&mutex); printf( “Hilo 1 avisa.\n” ); pthread_cond_signal(&seg); pthread_mutex_unlock(&mutex); printf( "Termina hilo 1.\n" ); pthread_exit(0); } void *hilo2() { pthread_mutex_lock(&mutex); printf( “Hilo 2 espera.\n” ); pthread_cond_wait(&seg,&mutex); printf( “Hilo 2 avisa.\n” ); pthread_cond_signal(&ter); pthread_mutex_unlock(&mutex); printf( "Termina hilo 2.\n" ); pthread_exit(0); }

void *hilo3() { pthread_mutex_lock(&mutex); printf( “Hilo 3 espera.\n” ); pthread_cond_wait(&ter,&mutex); printf( “Hilo 3 liberado.\n” ); pthread_mutex_unlock(&mutex); printf( "Termina hilo 3.\n" ); pthread_exit(0); } void main() { pthread_t h1,h2,h3; pthread_create(&h1,NULL,hilo1,NULL); pthread_create(&h2,NULL,hilo2,NULL); pthread_create(&h3,NULL,hilo3,NULL); printf( "Hilos creados.\n" ); pthread_mutex_lock(&mutex); pthread_cond_signal(&cond); printf( "Esperando hilos.\n" ); pthread_join(h1,NULL); printf( "Hilo 1 terminado.\n" ); pthread_join(h2,NULL); printf( "Hilo 2 terminado.\n" ); pthread_join(h3,NULL); printf( "Hilo 3 terminado.\n" ); pthread_mutex_unlock(&mutex); }

Indique qué se mostrará en pantalla al ejecutar el programa anterior, cuántos hilos terminarán la ejecución del código mostrado y en qué orden. Si algún hilo no consigue terminar indique por qué motivo no logra hacerlo. Asuma que todos los hilos tienen la misma prioridad y se utiliza un algoritmo de planificación FCFS.

Solución Salida en pantalla: 1) Hilos creados. 2) Esperando hilos. No termina ningún hilo. El principal se queda suspendido en “pthread_join(h1,NULL);” pero sigue teniendo el mutex cerrado. Los hilos h1, h2 y h3 se quedan suspendidos al realizar su “pthread_mutex_lock(&mutex);” para tratar de adquirir el mutex.

Q25) Repita la cuestión anterior, pero ahora utilizando un algoritmo de planificación por prioridades estáticas expulsivas, donde las prioridades siguen este orden h1 > h2 > h3 > main. Es decir, “h1” es el más prioritario y “main” el menos.

Solución Salida en pantalla: 1) Hilo 1 espera. 2) Hilo 2 espera. 3) Hilo 3 espera. 4) Hilos creados. 5) Esperando hilos. No termina ningún hilo. El hilo h1 se ha suspendido al realizar el “pthread_cond_wait(&cond, &mutex);” pues la condición todavía no ha ocurrido. Lo

21

mismo sucede con los hilos h2 y h3 con sus primeras llamadas a esa misma operación sobre sus respectivas condiciones. No hay problemas con el mutex pues al suspenderse en la condición queda abierto. Finalmente, el hilo principal, tras haber creado a todos los anteriores, se suspende en el mismo lugar que en la cuestión anterior, por lo que todos los hilos han quedado suspendidos.

Q26) Supongamos que en POSIX no existe la actual definición de los semáforos (la que utiliza el tipo “sem_t”). Complete la siguiente implementación de semáforos generales utilizando mútex y variables condición.

typedef struct { int contador; pthread_mutex_t m; pthread_cond_t c; } semaforo; void inicializar_semaforo (semaforo *s, int valor) { s->contador = valor;

pthread_mutex_init(&(s->m), NULL); pthread_cond_init(&(s->c), NULL);

}

void V (semaforo *s) { pthread_mutex_lock(&(s->m)); s->contador++; pthread_cond_signal(&(s->c)); pthread_mutex_unlock(&(s->m)); }

Solución void P (semaforo *s) { pthread_mutex_lock(&(s->m)); s->contador--; if (s->contador < 0) pthread_cond_wait(&(s->c),&(s->m)); pthread_mutex_unlock(&(s->m)); }

Q27) Sean tres procesos P1, P2 y P3 que ejecutan el código que se presenta en la tabla. Si todos los semáforos tienen valor inicial 1, indique si es posible que se produzca algún interbloqueo y por qué.

P1 P2 P3 A1; P(s1); P(s2); A2; P(s3); A3; V(s3); V(s2); V(s1);

P(s1); B1; P(s2); P(s3); B2; V(s3); V(s2); V(s1);

C1; P(s1); C2; P(s2); C3; P(s3); C4; V(s3); V(s2); V(s1);

Solución

22

No es posible porque todos los semáforos se están pidiendo en el mismo orden en todos los procesos. Por tanto, se cumple la regla de ordenación global de los recursos (en este caso las secciones protegidas por los semáforos) y petición también ordenada de éstos. Con esto se rompía la condición de Coffman de “espera circular” explicada dentro de las técnicas de prevención. Como se ha impedido que se cumpla una de las cuatro condiciones necesarias para que aparezca un interbloqueo, éste no podrá darse.

Q28) Sea el siguiente listado de un directorio en un sistema POSIX. En dicho directorio están disponibles todos los permisos (“r”, “w” y “x”) para todos los usuarios. d-wxrw---x 2 jorge gr1 1024 May 23 10:03 prueba -r----s--x 1 jorge gr3 4568 May 10 08:49 prog -r-----r-- 1 jorge gr1 7846 May 09 15:01 fich1 -r---w-r-- 1 marta gr3 456 Mar 19 23:58 fich2 Indique en las casillas de la tabla siguiente si el usuario y grupo que hay en cada columna podrá completar con éxito las órdenes que hay en las diferentes filas. Para ello escriba “SÍ” si puede completarse con éxito o “ERROR” si la orden fallara. Asuma que el ejecutable “prog” lee la información del fichero “fich1” y la añade al fichero “fich2” y que todos los ficheros mencionados en el directorio “prueba” ya existen y tienen permisos de lectura y escritura para todos los usuarios. (jorge, gr1) (javi, gr1) (marta, gr2) (pedro, gr2) ls prueba/exe ls >> prueba/va rm prueba/exe prog fich1 fich2

Solución En la primera fila se necesita permisos de lectura y ejecución sobre el directorio “prueba”. Por ello, ninguno de los usuarios que aparecen en las cuatro columnas podrá completar esta orden con éxito pues a todos les falta alguno de los dos permisos citados. En la segunda fila se necesitarían los permisos de lectura y ejecución sobre el directorio actual para poder efectuar el “ls” (el enunciado nos dice que todos los usuarios poseen tales derechos) y también se requiere el permiso de ejecución sobre el directorio “prueba” y el de escritura sobre el fichero “va” (este último permiso dice el enunciado que también lo tienen todos los usuario). Por ello, “jorge” puede completar con éxito la orden, pues tiene permiso de ejecución en el directorio “prueba” al ser su propietario. El usuario “javi” no podrá, pues está en la categoría del grupo a la hora de acceder al directorio “prueba” y esa clase carece de permiso de ejecución. Por último, tanto “marta” como “pedro” pertenecen a la clase de “otros” y sí que pueden completar la orden, pues pueden utilizar el permiso de ejecución. En la tercera fila intentamos borrar un fichero presente en el directorio “prueba”. Para ello se necesita el permiso de escritura y el de ejecución sobre tal directorio. El usuario “jorge” puede completar la orden, pues es el propietario y en esa clase aparecen ambos derechos. Ninguno de los demás usuarios podrá tener éxito, pues “javi” pertenece al grupo y en él no hay permiso de ejecución, mientras que “marta” y “pedro” están en la clase de “otros” y en ella no tenemos permiso de escritura. En la cuarta fila necesitaremos el permiso de ejecución sobre el ejecutable “prog”. Una vez se empieza a ejecutar el programa, como éste tiene activo el bit de SETGID, el proceso resultante adoptará como GID efectivo “gr3”. Con la nueva identidad adoptada se debe tener derecho para poder leer “fich1” y poder escribir en “fich2”. El usuario

23

“jorge” no puede tener éxito al intentar esta orden, ya que es el propietario y éste no tiene permiso de ejecución sobre el fichero “prog”, con lo que falla. Todos los demás usuarios sí que podrán, al menos, ejecutar dicho programa, pues están en la categoría de “otros” y sí tienen permiso de ejecución. El usuario “javi” podrá completar los otros dos pasos con éxito, pues el proceso resultante tiene identidad (javi, gr3), con lo que puede leer “fich1” (está en la clase “otros” y en ella tenemos permiso de lectura) y escribir en “fich2” (está en el clase “grupo” y en ella tenemos permiso de escritura). Al usuario “pedro” le ocurre exactamente lo mismo, pudiendo completar la orden. Por último, “marta” también podría leer “fich1”, pero fallaría a la hora de escribir sobre el fichero “fich2” pues es la propietaria de tal fichero y en dicha clase de usuarios el fichero no tiene permiso de escritura.

CUESTIONES PROPUESTAS C1) Sea el siguiente sistema de ficheros UNIX, donde no existen más directorios que los listados:

1 . 2 . 3 . 4 . 5 . 7 . 1 . . 1 . . 1 . . 2 . . 2 . . 4 . . 2 usr 4 local 6 f1.txt 7 bin2 8 cp 8 micp 3 dir 5 bin1

Indique para cada fichero el valor de su atributo número de enlaces: C2) Indique si las siguientes afirmaciones son verdaderas o falsas para un sistema Unix:

La única forma de crear un proceso nuevo (con distinto PID) es mediante la llamada al sistema "exec".

El mandato "ps -la" se utiliza para visualizar los atributos de todos los ficheros del directorio actual, incluidos los ocultos.

Si en un sistema de archivos UNIX existen dos entradas de directorio que asocian dos nombres distintos a un único número de nodo-i, se dice que el fichero al que hacen referencia tiene dos enlaces físicos.

Al redirigir la salida estándar en "cat hola > fich.txt", se debe modificar el descriptor de fichero número 0 para que en lugar de referirse al fichero especial /dev/tty se refiera a fich.txt.

Es posible que los siguientes dos nombres (o vías de acceso) absolutos se refieran al mismo fichero: /bin/ls y /home/valencf/bin/listar

Cualquier señal que se envíe a un proceso produce su terminación.

C3) Se tienen tres procesos P1, P2 y P3 que ejecutan el siguiente código (se asume que las variables a, b y c están compartidas por esos procesos y su valor, antes de que ninguno de ellos empezara a ejecutar el código presentado, era para todas ellas cero).

P1 P2 P3 a = 3; c = 1; b = 2; b = b + 3;

24

c = c * 2; c = c * 2; c = c * 2; a = a * 2; print(a,b,c); print(a,b,c);

SE PIDE: a) Sustituya cada símbolo @, por una operación P ó V sobre un semáforo con el fin de asegurar todas las condiciones siguientes: 1) La variable b debe mantener todavía el valor cero cuando la a valga 6. 2) El proceso P3 debe modificar el valor de b cuando P2 ya lo haya actualizado, pero nunca antes. 3) Debe garantizarse exclusión mutua en el acceso a la variable c. 4) Antes de que P2 y P3 muestren el valor de las variables todos los procesos han debido concluir las modificaciones de estas variables. b) Indique qué valor inicial deben tener todos los semáforos que haya utilizado.

Valores iniciales:

P1 P2 P3 @

b = 2; @

a = 3; @ b = b + 3;

c = 1; @ c = c * 2;

c = c * 2; c = c * 2; @ a = a * 2; @ @ @ print(a,b,c); print(a,b,c);

C4) Un programador ha protegido una sección de código de su programa mediante un semáforo, utilizando una llamada a la operación P() antes de entrar en la sección y una llamada a la operación V() cuando se abandona la sección. En ese programa se crean 10 hilos de ejecución que van a tener acceso a esa sección. Indique si las siguientes afirmaciones son verdaderas o falsas:

La sección protegida sólo podrá ser ejecutada por un único hilo de ejecución al mismo tiempo, independientemente del valor inicial de S.

Los hilos de ejecución podrán suspenderse al ejecutar la operación V() que cierra la sección, dependiendo del valor del contador del semáforo.

Los hilos de ejecución no se suspenderán en ningún caso, ya que los semáforos sólo sirven para sincronizar procesos.

Dependiendo del valor al que se inicializó el semáforo puede que ninguno de los hilos tenga que suspenderse al utilizar esa sección, incluso si todos ellos la ejecutan a la vez.

C5) En la solución vista en clase al problema del productor-consumidor, resuelta en el lenguaje C utilizando POSIX Threads, se ha cometido un error al codificar las funciones que acceden al buffer. Este error se encuentra en las siguientes líneas de código:

void poner (int id, int x) { pthread_mutex_lock(&mutex); while (Contador >= N) pthread_cond_wait(&lleno,&mutex);

void sacar (int id, int *x) { pthread_mutex_lock(&mutex); while (Contador >= N) pthread_cond_wait(&vacio,&mutex);

25

V[entrada] = x; entrada = (entrada + 1) % N; contador = contador + 1; pthread_cond_broadcast(&vacio); pthread_mutex_unlock(&mutex); }

*x = V[salida]; salida = (salida + 1) % N; contador = contador - 1; pthread_cond_broadcast(&lleno); pthread_mutex_unlock(&mutex); }

Asumiendo que se ejecutan una única tarea productor y una única tarea consumidor y que el buffer inicialmente está vacío, indique si las siguientes afirmaciones son verdaderas o falsas.

La tarea Consumidor no podrá sacar nunca elementos del buffer

La tarea Consumidor puede sacar elementos del buffer incorrectos.

Puede suceder un interbloqueo que afecte a ambas tareas.

El buffer se comportará correctamente si a partir si a partir del estado inicial se alternan estrictamente las operaciones Poner, Sacar, Poner, Sacar, Poner, Sacar, Poner, Sacar....

Sucederá un interbloqueo en cuanto ambas tareas llamen por primera vez a sus operaciones respectivas.

La tarea Productor podrá introducir elementos en el buffer cuando éste se encuentre lleno.