PROBLEMAS DE SO2

Anuncio
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);
sem_post(&turno);
/* Verificar si podemos pasar.
sem_wait(&mutex1);
/*
/*
/*
/*
/*
/*
/*
coches++;
if (coches==1)
sem_wait(&libre);
sem_post(&mutex1);
*/
Exclusión mutua en el acceso */
a la variable coches.
*/
Incrementar el nº de coches. */
Bloquear paso a los barcos, o*/
a los siguientes coches, si */
ya hubiese barcos.
*/
Fin de la S.C. sobre coches. */
bajar_puente();
pasar();
/* Funciones ya definidas.
*/
sem_wait(&mutex1);
coches--;
if (coches==0)
sem_post(&libre);
sem_post(&mutex1);
/*
/*
/*
/*
/*
*/
*/
*/
*/
*/
S.C. para acceder a coches.
Decrementar nº de coches.
Si es el último, libera el
paso a los barcos.
Fin de la S.C. sobre coches.
}
/*-----------------------------------------------------*/
/* Código de los barcos.
*/
/*-----------------------------------------------------*/
void *barco(void *arg) {
1
sem_wait(&mutex2);
barcos++;
if (barcos==1) {
sem_wait(&turno);
sem_wait(&libre);
}
sem_post(&mutex2);
/*
/*
/*
/*
/*
/*
/*
S.C. para acceder a "barcos".*/
Incrementar nº de barcos.
*/
Si es el primero, bloquea el */
paso a los coches, o al resto*/
de barcos, mientras haya co- */
ches.
*/
Fin de la S.C. sobre barcos. */
/*
/*
/*
/*
S.C. para acceder a barcos.
Decrementar nº de barcos.
Si es el último, libera el
paso a los coches.
levantar_puente();
pasar();
sem_wait(&mutex2);
barcos--;
if (barcos==0) {
sem_post(&turno);
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. *)
2
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++;
3
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. */
4
pasar_puente();
/* Proceder a cruzar.
*/
sem_wait(&mutex1);
norte--;
if (norte==0)
sem_post(&libre);
sem_post(&mutex1);
/* S.C. para acceder a “norte”. */
/* Si es el último, permite que */
/* los del sur pasen.
*/
/* 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);
sur--;
if (sur==0)
sem_post(&libre);
sem_post(&mutex2);
/* S.C. para acceder a “sur”.
*/
/* Si es el último, permite que */
/* los del norte pasen.
*/
/* 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);
5
*/
}
/*-----------------------------------------------------*/
/* 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á*/
6
/* 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 );
}
7
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.
8
#include
#include
#include
#include
<semaphore.h>
<pthread.h>
<stdlib.h>
<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
#include
#include
#include
<semaphore.h>
<pthread.h>
<stdlib.h>
<stdio.h>
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 );
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);
}
}
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);
}
9
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
Proceso B
Proceso C
P(S2);
P(S3);
x = y * 2;
y = y + 1;
V(S3);
P(S1);
P(S3);
x = x + 1;
y = 8 + x;
V(S2);
V(S3);
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).
void *Proceso2()
{
while(1)
{ SeccionRestante2;
EntraB();
SC2;
SaleB();
}
}
#include <semaphore.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#define NUM_HILOS 20
void
void
void
void
void
EntraA();
EntraB();
EntraC();
SaleA();
SaleB();
void *Proceso3()
10
{
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 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);
}
}
while (1)
{SeccionRestante3;
EntraC();
SC3;
SaleC();
}
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.
11
SC1
SC2
SC3
SC1
X
X
---
SC2
X
-----
SC3
----X
Solución
a)
SC1
SC2
SC3
SC1 SC2
X
--X
X
--
SC3
X
---
b)
void *Proceso2()
{
while(1)
{ SeccionRestante2;
EntraC();
SC2;
SaleC();
}
}
#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();
void *Proceso3()
{
while (1)
{SeccionRestante3;
EntraB();
SC3;
SaleB();
}
}
pthread_mutex_t mutex;
pthread_cond_t mutexA, mutexB, mutexC;
int nA, nB, nC;
void *Proceso1()
{
while(1)
{ SeccionRestante1;
EntraA();
SC1;
SaleA();
}
}
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.
12
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
Descriptor 1
Descriptor 2
carta
/dev/tty (la salida estándar que herede del shell)
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
#include
#include
#include
<semaphore.h>
<pthread.h>
<stdlib.h>
<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
13
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?
14
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
B1
turno
-1
C1
libre
-1
B0
En 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, C3
libre
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
#include
#include
#include
void cerrar(void) {
pthread_mutex_lock(&mutex);
cerrada = 1;
pthread_mutex_unlock(&mutex);
}/*fin cerrar*/
<semaphore.h>
<pthread.h>
<stdlib.h>
<stdio.h>
pthread_mutex_t mutex;
pthread_cond_t cola;
int cerrada;
int main(void) {
cerrada = 1
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cola,NULL);
void pasar(void) {
pthread_mutex_lock(&mutex);
:
:
if (cerrada)
pthread_cond_wait(&cola,&mutex);
pthread_cond_signal(&cola);
} /*fin main*/
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);
}
15
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:
procedure entry cerrar;
begin
cerrada := TRUE
end;
type barrera=monitor
var cola: condition;
cerrada: boolean;
procedure entry pasar;
begin
if cerrada then cola.wait;
cola.signal
end;
begin
cerrada := TRUE
end.
procedure entry abrir;
begin
if cerrada then cola.signal;
cerrada := FALSE
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
P(S);
x = x+1;
P(S3);
x = x–2;
H2
V(S);
P(S);
x = 4;
V(S3);
H3
V(S2);
x = x+2;
V(S3);
x = x*3;
Solución
16
H4
P(S2);
V(S2);
P(S);
V(S);
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:
R0 R1 R2 R3 R4 RC
Recurso
1
1
1
1
1
Cantidad
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
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;
Perfil de los procesos impares
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
17
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
Operaciones1;
P2
P3
Operaciones3;
Operaciones3;
Operaciones4;
Operaciones2;
Operaciones5;
Solución
Valores iniciales: Todos los semáforos a cero.
P1
Operaciones1;
Operaciones2;
V(S1);
V(S1);
P2
P3
P(S1);
Operaciones3;
P(S2);
Operaciones4;
V(S3);
P(S1);
Operaciones3;
V(S2);
P(S3);
Operaciones5;
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
18
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
A1;
B2;
A4;
B5;
P2
B1;
B4;
A2;
P3
B3;
A3;
B6;
Solución
Las instrucciones a utilizar aparecen en la tabla siguiente:
P1
A1;
P(S1);
B2;
V(S2);
P(M);
A4;
V(M);
P(S4);
B5;
V(S5);
P2
B1;
V(S1);
P(S3);
B4;
V(S4);
P(M);
A2;
V(M);
P3
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.
19
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 *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);
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);
}
}
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
20
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
A1;
P(s1);
P(s2);
A2;
P(s3);
A3;
V(s3);
V(s2);
V(s1);
P2
P(s1);
B1;
P(s2);
P(s3);
B2;
V(s3);
V(s2);
V(s1);
P3
C1;
P(s1);
C2;
P(s2);
C3;
P(s3);
C4;
V(s3);
V(s2);
V(s1);
Solución
21
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
-r----s--x
-r-----r--r---w-r--
2
1
1
1
jorge
jorge
jorge
marta
gr1
gr3
gr1
gr3
1024
4568
7846
456
May
May
May
Mar
23
10
09
19
10:03
08:49
15:01
23:58
prueba
prog
fich1
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
22
“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
1
2
3
.
..
usr
dir
2
1
4
5
.
..
local
bin1
3
1
6
.
..
f1.txt
4
2
7
.
..
bin2
5
2
8
.
..
cp
7
4
8
.
..
micp
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
a = 3;
c = 1;
P2
b = 2;
P3
b = b + 3;
23
c = c * 2;
a = a * 2;
c = c * 2;
print(a,b,c);
c = c * 2;
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) {
void
sacar (int id, int *x) {
pthread_mutex_lock(&mutex);
pthread_mutex_lock(&mutex);
while (Contador >= N)
pthread_cond_wait(&lleno,&mutex);
while (Contador >= N)
pthread_cond_wait(&vacio,&mutex);
24
V[entrada] = x;
entrada = (entrada + 1) % N;
contador = contador + 1;
*x = V[salida];
salida = (salida + 1) % N;
contador = contador - 1;
pthread_cond_broadcast(&vacio);
pthread_mutex_unlock(&mutex);
}
}
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.
25
Descargar