Examen de Fundamentos de sistemas distribuidos

Anuncio
Examen de Fundamentos de sistemas distribuidos
Tiempo total: 2 horas
Problema: Programa: Rendezvous con semáforos(5 puntos)
Utilizando como único mecanismo de sincronización los semáforos descritos en semacquire(2), es decir:
int semacquire(long *addr, int block);
long semrelease(long *addr, long count);
implemente una biblioteca rendez que ofrezca el mecanismo de sincronización rendezvous(2), pero intercambiando una cadena de texto. La interfaz, descrita en un fichero de cabeceras rendez.h, debe ser la
siguiente:
void initrendez(void);
char *rendez(int tag, char* msg);
La función initrendez sirve para inicializar la biblioteca y puede no hacer nada si no es necesario.
Cuando un proceso llama a rendez con un tag, dicho proceso se debe bloquear hasta que otro proceso
llame a rendez con el mismo tag. En ese momento, el parámetro msg que ha suministrado el primer
proceso será el valor devuelto por rendez para el segundo proceso (y viceversa). Si un tercer proceso
llama a rendez con el mismo tag, se quedará bloqueado de forma similar al primer proceso hasta que un
cuarto proceso llame a rendez y así sucesivamente. Dicho de otro modo, rendez permite a dos procesos sincronizarse e intercambiar punteros a char de forma ligada a un número (tag) dado. El mensaje sólo se
intercambiará copiando el valor del puntero, no su contenido.
Impleméntese aparte un programa de prueba llamado main.c.
-2-
Solución

_______
_rendez.h
void initrendez(void);
char *rendez(int tag, char* msg);
_________
rendez9.c 

#include <u.h>
#include <libc.h>
#include "rendez.h"
typedef struct Rz Rz;
struct Rz {
int
tag;
long wait;
char*
value;
Rz *next;
};
long mutex = 1;
Rz
*rz;
void
initrendez(void)
{
}
Rz *
taketag(int tag)
{
Rz
*r, *p;
p = nil;
for(r = rz; r != nil; r = r->next)
{
if(r->tag == tag){
if(p == nil)
rz = rz->next;
else
p->next = r->next;
return r;
}
p = r;
}
return nil;
}
char *
rendez(int tag, char *msg)
{
int
i;
Rz
*r;
char
*omsg;
-3-
semacquire(&mutex, 1);
r = taketag(tag);
if(r != nil){
omsg = r->value;
r->value = msg;
semrelease(&r->wait, 1);
semrelease(&mutex, 1);
return omsg;
}
r = malloc(sizeof(Rz));
if(r == nil)
sysfatal("no memory");
r->tag = tag;
r->value = msg;
r->wait = 0;
r->next = rz;
rz = r;
semrelease(&mutex, 1);
semacquire(&r->wait, 1);
omsg = r->value;
free(r);
return omsg;
}
Un único mutex protege la estructura de datos, compuesta de un array rz (con una entrada por rendezvous
en curso) y un índice nrz que indica el número de entradas en uso en el array. En realidad, el código
admite que haya entradas nulas bajo &rz[nrz], cosa que ocurre cuando una entrada en uso ha dejado de
estarlo. El array rz se hace crecer de Incr en Incr entradas cuando es preciso.
Tras obtener el mutex, se escanea el array buscando una entrada con el tag suministrado. Si esa
entrada existe, somos el segundo en llamar a rendez con ese valor y lo que hacemos es intercambiar los
valores. En caso contrario inicializamos una entrada en el array y esperamos con un down(r->wait) a
que otro proceso llame con el mismo valor.
Nótese que el primer proceso (el que duerme) es el responsable de liberar los recursos. No obstante,
el segundo proceso quita dichos recursos del array para evitar condiciones de carrera.
-4-
Problema: Cuestiones de teoría
Responda en un folio de examen las siguientes cuestiones:
A)
Memoria virtual (2 puntos) Ponga un ejemplo de tablas de páginas de dos procesos que comparten
memoria. Suponga páginas de 4K, el tipo de tabla de páginas que quiera y una organización de la
memoria virtual similar a la de Plan 9, por ejemplo:
Stack
Text
Data
Bss
R
defff000
00001000
00012000
00017000
dffff000
00012000
00017000
0002f000
1
8
1
1
¿Sucede algo similar cuando con la tabla de páginas cuando se llama a fork? ¿Por qué? Responda
razonadamente.
-5-
Solución
Cuando dos procesos comparten memoria lo que sucede es que hay entradas de la tabla de páginas de los
diferentes procesos que apuntan a las misma direcciones físicas. Si vamos al caso más sencillo de tablas de
páginas de un nivel, y los procesos sólo tienen cuatro páginas, una de pila, una de texto, una del BSS y una
de datos el resto de entradas serán cero (no tendrán instalada traducción). Suponiendo que las páginas son
de 4K, la tabla podría ser algo como (suponiendo que el bit 5 puesto a 1 hace que la página sea de escritura)
y que el resto de los bits son cero:
[00000] = 0
[00001] = 0x0003f000
[00002] = 0
[00011] = 0
[00012]
[00013]
[00017]
[00018]
[00019]
[0001a]
[0002f]
[defff]
[dffff]
=
=
=
=
=
=
=
=
=
0x0005f010
0
0
0
0x0005f010
0
0
0
0x0002f010
El otro proceso tendrá las mismas entradas en todos los casos salvo que puede que no tenga alguna instalada o tenga alguna de más (se instalan en demanda) y que la pila nunca se comparte:
[00000] = 0
[00001] = 0x0003f000
[00002] = 0
[00011] = 0
[00012]
[00013]
[00017]
[00018]
[00019]
[0001a]
[0002f]
[defff]
[dffff]
=
=
=
=
=
=
=
=
=
0x0005f010
0
0
0x0006f010
0x0005f010
0
0
0
0x00025010
Cuando se llama a fork, hay varias razones por las que se puede compartir memoria y sucede lo mismo. El
segmento de texto, se suele compartir siempre, ya que es de de sólo lectura. Como parte del proceso de
hacer fork, el segmento datos y el BSS se comparten tras cambiarles la protección a todas las páginas a sólo
lectura. Cuando haya un fallo de protección por escritura se copian, se instalan las nuevas traducciones y se
desprotegen. Esto mecanismo se llama copy on write (COW). Aparte de todo esto, se puede compartir un
segmento, utilizando ensegattach(2) Plan 9 o mmap(2) en Unix.
B)
Segmentación (1.5 puntos) En un asignador de memoria que estamos utilizando, hay un bug y la
memoria que devuelve al usuario está en el segmento de texto. Sorprendentemente, el siguiente programa, funciona. Explique por qué y de dónde vienen la memoria de las diferentes variables.
-6-
#include <u.h>
#include <libc.h>
char datos[128];
void
main(int, char *[])
{
char *masdatos;
int i;
masdatos = malloc(16);
for(i = 0; i < sizeof(datos); i++)
datos[i] = ’z’;
memmove(datos, masdatos, 16);
for(i = 16; i < sizeof(datos); i++)
print("%c", datos[i]);
}
-7-
Solución
La única memoria que viene del asignador de memoria es a la que apunta el puntero masdatos. Esta
memoria sólo se lee (cuando se copia utilizando el memmove). Aunque debería venir del BSS (del heap,
que está en el BSS), el enunciado dice que por un bug viene del segmento de texto. Como el segmento de
texto es de sólo lectura, no hay problema con copiar memoria de él. El programa procede e imprime el
valor de datos sin problema. La memoria datos es global sin inicializar y por tanto viene del BSS. Las variables masdatos (el puntero, no la memoria a la que apunta) y la variable i vienen ambas de la pila y por
tanto del segmento de pila al ser variables locales.
C)
Sistemas de ficheros (1,5 puntos) En un sistema de ficheros con inodos sólo con punteros directos,
se ha borrado completamenteun inodo de la tabla de inodos (si hay varias copias de la tabla de inodos, se ha borrado en todas) . Responda reazonadamente ¿Qué se ha perdido exactamente? Llevamos el disco duro a una empresa de recuperación de datos y nos dan un listado de los nombres de los
ficheros y directorios y no falta ninguno. ¿Es esto posible? Responda razonadamente.
-8-
Solución
Si se ha perdido un inodo, se ha perdido un fichero, directorio o valor de un enlace simbólico junto con sus
metadatos (salvo el nombre). Un inodo contiene los metadatos (permisos, fechas de modificación, dueño
etc.) y los punteros a los datos. Se ha perdido, pues (metadatos aparte), la asociación entre los bloques de
datos (apuntados por el inodo) y el elemento, un fichero, directorio o enlace simbólico. Más concretamente,
qué bloques pertenecen al elemento y en qué orden. Si es un directorio, hemos perdido las entradas de
directorio que contienen los bloques de datos, es decir, nombres y números de inodo. En este caso hemos
perdido los nombres de todo lo que contenía, es decir ese nodo del árbol (aunque el subárbol se puede recuperar mirando qué inodos se han quedado huérfanos, a menos que hubiese un enlace duro a ellos, en cuyo
caso, sencillamente están ya presentes en otro punto del sistema de ficheros, pero no hay manera de recuperar la correspondencia).
Es posible que los bloques sigan existiendo y se puedan quizás recuperar, aunque depende del contenido del
fichero o directorio que esto se pueda interpretar, ya que hemos perdido en qué orden van y el tamaño del
fichero o directorio.
Si es un enlace simbólico, hemos perdido el nombre al que apuntaba. De nuevo, en ambos casos, es posible
que se puedan recuperar los bloques de datos, que no se han borrado. Si el inodo que se ha borrado es el de
un fichero no se ha perdido más que su contenido. El nombre está en la entrada de directorio del directorio
que lo contiene, con lo cual, no se ha perdido. En este caso, la empresa puede estar diciendo la verdad.
Si es el inodo correspondiente a un directorio, deberían haberse perdido nombres (los de los datos que
contenía), aunque como hemos dicho antes, quizás se puedan recuperar mirando qué bloques están ocupados. No sabiendo el tamaño, es difícil saber qué nombres realmente se estában usando y cuales no. En este
caso lo que dice la empresa es más difícil que sea posible.
Descargar