teoría y prácticas

Anuncio
Examen de Fundamentos de sistemas operativos
Tiempo total: 2 horas.
Problema: Implementación de canales con Rendez (monitores)
Implemente canales con buffering para enteros mediante variables condición Rendez. Para ello, debe crear
un tipo de datos Canal. La interfaz de los canales debe ser la siguiente:
Canal * ccreate(int size);
void
csend(Canal *c, int i);
int
creceive(Canal *c);
void
cfree(Canal *c);
-2-
Solución

_________
_channels.c
#include <u.h>
#include <libc.h>
typedef struct Canal Canal;
struct Canal {
QLock;
int exiting;
int nproc;
int sz;
int hd,tl;
int *q;
Rendez full;
Rendez empty;
};
Canal*
ccreate(int sz)
{
Canal
*c;
c = mallocz(sizeof(*c), 1);
if(c == nil)
return nil;
/* needs +1 to signal that queue is full */
c->q = malloc(sizeof(int) * (sz+1));
if(c->q == nil){
free(c);
return nil;
}
c->full.l = c;
c->empty.l = c;
return c;
}
int
next(int i, int n)
{
return (i+1)%n;
}
-3-
void
csend(Canal *c, int i)
{
qlock(c);
c->nproc++;
while(next(c->hd, c->sz) == c->tl && !c->exiting)
rsleep(&c->full);
if(c->exiting == 0){
c->q[c->hd] = i;
c->hd = next(c->hd, c->sz);
rwakeup(&c->empty);
}
c->nproc--;
qunlock(c);
}
int
creceive(Canal *c)
{
int r;
r = -1;
qlock(c);
c->nproc++;
while(c->hd == c->tl && !c->exiting)
rsleep(&c->empty);
if(!c->exiting){
r = c->q[c->tl];
c->tl = next(c->tl, c->sz);
rwakeup(&c->full);
}
c->nproc--;
qunlock(c);
return r;
}
void
cfree(Canal *c)
{
qlock(c);
c->exiting = 1;
while(c->nproc > 0){
rwakeup(&c->full);
rwakeup(&c->empty);
qunlock(c);
sleep(0);
qlock(c);
}
qunlock(c);
free(c->q);
free(c);
}
El canal se crea como un buffer de enteros que se mantiene en q protegido por un QLock y ayudado de dos
variables-condición Rendez descritas en lock(2) para dormir a los procesos que esperan debido a que el
buffer está lleno o vacío. En realidad, el problema es un típico productor-consumidor con el buffer acotado.
La única diferencia reseñable es que tanto csend como creceive prestan atención a que el canal se esté
-4-
destruyendo cuando aún hay procesos utilizándolo. En tal caso, el campo exiting señala tal suceso y la
función cfree espera (con espera activa) a que ningún proceso siga utilizando el canal.
Entrega: Copie un fichero llamado login.c, donde login es tu nombre de usuario, en el directorio
/usr/elf/examen. ATENCIÓN: Sólo se puede efectuar la entrega una vez. Ponga un comentario
con su nombre y apellidos en el fichero.
Problema: Cuestiones de teoría
Responda en un folio de examen las siguientes cuestiones:
A)
(2 puntos) Estamos depurando un programa y sabemos que el resultado de ejecutar el siguiente
comando sobre el directorio de /proc correspondiente a un proceso ejecutando el programa
(recuerde que en el fichero segment se muestran las direcciones de comienzo y fin de los segmentos, ver proc(3) ) es:
;cat /proc/345/segment
Stack
defff000 dffff000
Text
R 00001000 00011000
Data
00011000 00013000
Bss
00013000 0002b000
1
8
1
1
Una parte del resultado de ejecutar nm sobre el binario es:
1d3e
8eac
2d68
82ee
32ae
32e2
d018
d1ac
e08c
e104
T
T
T
T
t
t
D
d
d
d
strlen
strncpy
strrchr
xdata
swapb
swapi
swidth
tab
tab1
tab2
Responda a las siguientes preguntas justificando las respuestas: ¿Qué cree que sucedería si se ejecutase el siguiente código en una función del programa anterior?
int a;
char *p;
p = (char *)0x11d3c;
p[2] = ’c’;
print("%p\n", &a);
Explique detalladamente y paso a paso la intervención del sistema operativo en caso de haberla. ¿Las
direcciones mostradas en nm son virtuales? ¿Y las que imprime el trozo de código anterior? ¿Entre
qué valores estaría la dirección mostrada en el código anterior?
-5-
Solución
Tanto las direcciones mostradas por el programa como las mostradas por nm son virtuales, que son las
únicas a las que tiene acceso el proceso. Todo el proceso de compilación supone que las direcciones son
virtuales, y por eso el enlazador tiene libertad para elegir en qué posiciones (dentro del modelo de segmentos que ofrece el sistema operativo) pone los símbolos. La dirección impresa por el programa es la de una
variable local que vendrá de la pila, luego estará en el rango 0xdefff000 y 0xdffff000.
B)
(1.5 puntos) Se le ha pedido que escriba una aplicación que recupere todos los ficheros de una
partición formateada con un sistema de ficheros basado en inodos en el que se han perdido todos los
bloques de datos apuntados por los inodos que representan directorios (los ha borrado un troyano).
Explique detalladamente qué podría hacer su aplicación, qué datos se podrían recuperar, y cuáles no y
por qué.
-6-
Solución
Al haber perdido los bloques de datos de los directorios se han perdido las entradas de directorios. En ellas
se encuentran los nombres de los ficheros y directorios con el número de inodo al que apunta la entrada de
directorio. Esto es lo único que se ha perdido. Como consecuencia, de forma implícita, se ha perdido, la
jerarquía de directorios y ficheros.
Nuestra aplicación puede recorrerse la tabla de inodos para ver qué inodos están ocupados. De esta
forma puede recuperar completamente todos los ficheros con sus metadatos (contenidos en el inodo) y sus
datos, (el inodo apunta a los bloques de datos). No puede recuperar, sin embargo, sus nombres ni su
posición en la jerarquía. De los directorios sólo se pueden recuperar los metadatos, pero sin saber a qué
nombre de directorio pertenecen, ni su contenido, (que estaba en los bloques de datos) no serán demasiado
útiles.
C)
(1,5 puntos) Tiene un programa que reserva y libera continuamente estos dos tipos de datos:
stuct Buf {
char buf[2];
};
stuct Trail {
char trail;
};
Mide el uso de memoria y descubre que el programa utiliza 30 veces más memoria de lo que
esperaría (malloc devuelve un error diciendo de lo que hay memoria mucho antes de lo esperado).
Explique qué está sucediendo, cómo se llama el problema y cómo lo resolvería.
Escriba el login en el examen.
-7-
Solución
Dado que ambos tipos de datos son muy pequeños, seguramente haya un problema de fragmentación
interna, seguramente ambos tipos de datos son de un tamaño menor que el tamaño mínimo que reserva el
asignador. Una posible solución sería bajar el tamaño mínimo que reserva el asignador, pero esto tendría el
problema de generar fragmentación externa a su vez y cambiar el alineamiento de la memoria.
La mejor manera de resolver el problema (suponiendo que realmente necesito reservar trozos tan
pequeños) sería hacerme un asignador especializado que se ajuste más a las necesidades del programa. Este
asignador puede reservar trozos más grandes mediante malloc y repartir el espacio en el interior de forma
acorde.
Para el asignador especializado, una solución sencilla puede ser crear dos estructuras de datos, una
que contenga un array de tipo Buf y otra que contenga un array de tipo Trail, que puedo enlazar en
varias listas (llenas, con hueco, vacías). Dentro de esta estructura de datos tendré que anotar también (por
ejemplo en un bitmap), qué elementos están ocupados.
Descargar