Guía de la práctica memon

El objetivo de este documento es facilitar el arranque de esta práctica, dando algunas recomendaciones sobre los primeros pasos de la misma, que son los que más cuestan, en un documento relativamente corto, comparado con el enunciado oficial de la misma.

Activación del monitor

El monitor obtiene estadísticas sobre el uso de la memoria por parte de un programa durante su ejecución. Si se ejecuta el monitor de la siguiente manera:

./memon_FIFO 6 prog args...

Se está especificando que se va a monitorizar la ejecución del programa “prog” , que usa los argumentos “args…”, suponiendo que hay 6 marcos libres y que se usa un algoritmo de reemplazo FIFO.

Fundamento del monitor

Para obtener las estadísticas, el monitor impide el acceso a las regiones del proceso a monitorizar usando “mprotect”, cuyos parámetros son:

  • “dirección”: Dado que en la práctica se va a controlar el acceso a cada página, especificaremos la dirección de inicio de la página.
  • “tamaño”: El de una página, en nuestro caso.
  • “permisos”: Para impedir el acceso “PROT_NONE”.

Cuando el proceso ejecuta e intenta acceder a su mapa de memoria, se produce una violación de acceso (señal “SEGV”). En el tratamiento del “SEGV” está el núcleo de la práctica y es donde se llevarán las estadí­sticas de acceso.

Versión inicial vací­a de la función de tratamiento de “SEGV” (fichero “fallo.c”).

void fallo_pagina(void *dir_fallo) {
    printf("-------- fallo %p -------------\n", dir_fallo);
    return;
}

En ella se podrí­an incluir estadí­sticas:

void fallo_pagina(void *dir_fallo) {
    printf("-------- fallo %p -------------\n", dir_fallo);
    fallos_total++;
    return;
}

Pero, cuidado, dentro de la rutina tenemos que devolver los permisos, sino volverá a repetirse la violación de acceso a memoria puesto que se repite la misma instrucción que causó el fallo. Precisamente, el que se repita la misma instrucción es lo que hace que el monitor no sea intrusivo, es decir, que el programa a monitorizar realice la labor prevista.

void fallo_pagina(void *dir_fallo) {
    printf("-------- fallo %p -------------\n", dir_fallo);
    fallos_total++;
    .........
    **DEVOLVER PERMISOS CON MPROTECT**
    return;
}

¿Cómo saber el valor de los parámetros que hay que pasarle al “mprotect” que devuelve los permisos?:

  • “dirección”: La de comienzo de la página que ha causado el fallo. No es la dirección de fallo, sino la de inicio de la página de fallo. En el caso de un sistema con un tamaño de página de 4Kbytes y con direcciones de 32 bits, se puede calcular el comienzo de la página, a partir de la dirección de fallo, poniendo a 0 los doce bits de menor peso (algo como “pagina_fallo = dir_fallo & 0xFFFFF000”), pero no merece la pena ya que tenemos que acceder a la tabla de regiones y de páginas por motivos que veremos más adelante (además, no es una solución transportable a otros sistemas), y en esas estructuras está almacenado ese valor.
  • “tamaño”: El de una página.
  • “permisos”: Los permisos originales, que están en la tabla de regiones, que veremos enseguida.

Si hay un error en el código que incluye el alumno en la rutina “fallo_pagina” (o en cualquier otra de las que debe modificar), puede provocar su propio SEGV, activando la función “fallo_pagina” de forma anidada.

Colocar unos “printf” tal como se plantea a continuación y controlar el error en el “mprotect”:

void fallo_pagina(void *dir_fallo) {
    **printf("-------- fallo %p -------------\n", dir_fallo);**
    fallos_total++;
    .........
    **printf("-------- dir mprotect %p -------------\n", dir);**
    **if (mprotect(dir, ...)<0) {
        perror("Error devolviendo permisos");
	_exit(1);
    }**
    .........
    **printf("-------- FIN fallo %p -------------\n", dir_fallo);**
    return;
}

Si la práctica falla, observando la salida se pueden sacar conclusiones:

  • Se repite continuamente el primer mensaje para la misma dirección: o nos hemos olvidado el “mprotect” o lo estamos haciendo mal. En este último caso, podrí­a deberse a que hemos calculado mal la dirección de la página y le estamos devolviendo los permisos a otra página. Se puede detectar mirando qué dirección imprime el segundo mensaje (el del “mprotect”) comprobando que es igual a la dirección de fallo pero con los últimos tres dí­gitos hexadecimales a 0.
  • Si se anidan mensajes de fallo para direcciones diferentes sin aparecer mensajes de fin de fallo, hay un error en el código de la rutina de fallo que provoca ese anidamiento. Hay que depurarlo imprimiendo más mensajes para poder detectar dónde está el error.

Estructuras de datos

Existen las siguientes:

  • Tabla de regiones: Vector estático que guarda el estado de cada región (dirección inicial, tamaño, permisos originales de la región, dirección de la tabla de páginas de la región, …). Esta estructura ya está completa. Nótese que a la hora de recorrerla hay que saltarse aquellas entradas cuyo campo “usada” no esté activo.
  • Tabla de páginas: Hay una por cada región, referenciada desde la entrada de la región correspondiente. Se trata de un vector reservado en memoria dinámica con una entrada por cada página de la región (aunque esté reservado en memoria dinámica, es un vector y se puede usar como tal: “tabla_regiones[i].tabla_paginas[j].dir_inicial”). En la versión inicial está prácticamente vací­a. Sólo contiene la dirección de la página en cuestión (ese es justamente el valor que meterí­amos en el “mprotect” para devolver los permisos) y un puntero que hace referencia a la entrada de la región a la que pertenece esa página (según se diseñe la práctica, este puntero de vuelta puede ser algo que no se usa). A lo largo de la práctica, habrá que añadir nuevos campos.
  • Tabla de marcos: Está definida en el fichero “marcos.c” y, además de ya estar completa, no es preciso acceder a ella directamente, sino a través de las funciones exportadas por el módulo “marcos”.

Eventos

El monitor se activa por 4 eventos:

  • Se produce un SEGV: se activa la rutina de fallo, que inicialmente está vací­a y hay que completar.
  • Se ha creado una nueva región (“creacion_region”): Ya está programada la función que realiza toda la labor asociada a este evento (buscar una entrada libre en la tabla de regiones, rellenarla, crear la tabla de páginas reservándola en memoria dinámica, …). Dado que los campos de la entrada de la tabla de páginas los va añadiendo el alumno, esta función llama a “iniciar_entrada_tpag” (inicialmente vacía) por cada página de la nueva región, que, además de iniciar los campos, deberá impedir el acceso a la página usando “mprotect”
  • Se ha eliminado una región existente (“eliminacion_region”): Ya está programada la funcionalidad requerida. Esta función llama a “liberar_entrada_tpag” (inicialmente vacía) por cada página de la región que ha desaparecido.
  • Ha cambiado el tamaño de la región (“cmbio_tam_region”): el alumno tiene que programar esta funcionalidad.

Recapitulando, la práctica requiere completar cuatro funciones:

  • “fallo_pagina”.
  • “iniciar_entrada_tpag”.
  • “liberar_entrada_tpag”.
  • “cambio_tam_region”

Versión preliminar de la práctica

En el enunciado de la práctica se plantea una primera versión, pero, en esta guí­a, para hacer el trabajo de una manera más incremental, se propone una versión previa, más sencilla, que sólo obtendría una puntuación de 0,5, y que se describe a continuación.

Esta primera versión sólo pretende terminar la ejecución del programa, después de haberle quitado los permisos a todas las páginas de su mapa. De las cuatro funciones que hay que modificar, sólo se actuará sobre dos de ellas:

  • “iniciar_entrada_tpag”: Se incluirá una llamada a “mprotect” para quitar el permiso de acceso a la página.
  • “fallo”: Sólo se incluirá la lógica para comprobar si la dirección es válida (el programa a monitorizar puede ser erróneo) y, en caso de que lo sea, sólo se devolverán los permisos originales con “mprotect” (de paso podemos actualizar el valor del contador de fallos totales). Para detectar si la dirección es válida, se recorrerá la tabla de regiones comprobando si encaja en alguna de ellas. En caso negativo, será un error y se termina inmediatamente la monitorización. En caso de encontrar que está incluida en una región, hay que calcular en qué página de la región. Este cálculo se puede realizar recorriendo la tabla de páginas de la región o realizando una operación aritmética teniendo en cuenta la dirección de fallo, la dirección de inicio de la región y el tamaño de la página. Sabiendo la página y la región, se puede realizar el “mprotect” que devuelve los permisos.
void fallo_pagina(void *dir_fallo) {
    printf("-------- fallo %p -------------\n", dir_fallo);
    Encontrar región y página a la que pertenece la página
    if (no pertenece a ninguna región) {
        printf("acceso a memoria inválido %p\n", dir_fallo);
        _exit(1);
    }
    fallos_total++;
    printf("-------- dir mprotect %p -------------\n", dir);
    if (mprotect(dir_inicio_pagina, tam_pagina, permisos_region)<0) {
        perror("Error devolviendo permisos");
        _exit(1);
    }
    printf("-------- FIN fallo %p -------------\n", dir_fallo);
    return;
}

Una vez superada esta etapa, será más fácil afrontar las siguientes versiones planteadas en el enunciado.

Siguientes pasos

Como se comentó al principio, esta guí­a sólo describe y da consejos sobre el arranque de la práctica. Una vez completada esta versión preliminar, creemos que el alumno ya está familiarizado con la práctica y puede usar el enunciado de la misma para completar el trabajo. De todas formas, a continuación, se comentan algunos aspectos de las siguientes fases.

En la versión preliminar, el número de marcos y el algoritmo de reemplazo recibidos como parámetros no se utilizan. Evidentemente,en la primera versión planteada en el enunciado de la práctica sí que se van a usar. Concretamente, el número de marcos disponible establecerá cuál es el número máximo de páginas que pueden tener habilitado su permiso de acceso. Si se produce un fallo y se ha llegado a este máximo, habrá que aplicar el algoritmo de reemplazo especificado (que, en el caso del FIFO, ya está implementado) y expulsar a la página seleccionada por el mismo, quitándole el permiso de acceso con “mprotect”. Por tanto, el tratamiento realizado en la rutina de fallo se empieza a parecer al estudiado en la teoría de la asignatura:

void fallo_pagina(void *dir_fallo) {
    Comprobar que la dirección es válida
    Reservar un marco libre
    Si no hay ninguno {
        Aplicar el algoritmo de reemplazo que selecciona un marco
        Invalidar la página contenida en ese marco
    }
    Asociar la nueva página con el marco
    Poner como válida la página
}

Donde las operaciones de invalidar y poner como válida corresponden con quitar el permiso de acceso y devolvérselo, respectivamente.

Además, hay que completar la función “liberar_entrada_tpag”, dejando libre el marco de página ocupado por la misma en caso de que estuviera residente (por tanto, habrá que gestionar la información de si la página está residente y, en caso afirmativo, en qué marco).

En cuanto a la función de “cambio_tam_region”, si se trata de un aumento, habrá que iniciar todas las entradas de página de la parte expandida, mientras que si se trata de una disminución, se tendrán que liberar las páginas que desaparecen. Nótese que para redimensionar la tabla de páginas, habrá que usar la función “realloc”.

Por último, con respecto a la versión intermedia y sobre cómo gestionar un bit de modificado, ante un fallo, el truco consiste en devolverle los permisos originales menos el de escritura (usando la macro “PROT_CLR” definida en “apoyo.h”). Si se produce un fallo sobre la página estando ya residente, será que se está intentado modificar la página, por lo que en ese punto se puede asignar el bit de modificado.

void fallo_pagina(void *dir_fallo) {
    Si no está residente
        Código de la primera versión pero devolviendo los permisos originales menos el de escritura
    Si está residente
        Activar bit de modificado
        Devolver los permisos originales
}
 
docencia/asignaturas/dso/practicas/memom/guia.txt · Última modificación: 2009/10/13 15:37 por fperez
 
Recent changes RSS feed Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki