Stack Growth

Es hora de salir de tu pequeña burbuja, pequeño programador.

Repetiré la premisa: Un OS le permite a un proceso crecer, pero limita hasta cuánto debe crecer. En esta sección de la guía, nos enfocaremos en la implementación de esa premisa.

Un poco de Explicación...

Como sabemos, cuando el procesador está en la etapa de fetch, la dirección virtual que quiere consultar se traduce (empleando la Page Table del proceso actual) a una dirección física de la memoria RAM.

Durante el proceso de traducción, hay un breve momento que el OS realiza un tipo de sanity check a la página que el proceso quiere acceder (por el momento solo nos enfocaremos en una parte, el resto lo seguiremos viendo a lo largo de la guía). Lo primero que verifica es si el número de página se encuentra dentro de la Page Table. Si está, el proceso de traducción sigue sin ningún problema, pero si no encuentra el número de página, se genera un page fault.

Un page fault es un tipo de Interrupción Interna que maneja PintOS. Cuando ocurre, PintOS deja de hacer lo que estaba haciendo y atiende de manera inmediata el page fault.

Principalmente en esta parte estarás lidiando mucho con los page faults, ya que a partir de ellos se puede tomar la decisión de hacer crecer el proceso o no. (Es un buen momento de pausar la guía e ir a leer el archivo userprog/exception.c para que te des una idea sobre lo que estarás modificando).

Objetivo

Permitir el crecimiento de un proceso por medio de los page faults.

Implementación

Primera Parte: Sanity Check

Como buen estudiante, sabemos que ya fuiste a leer el archivo userprog/exception.c y notaste una función en particular que se llama page_fault(). Esta función se manda a llamar cada vez que ocurre un page fault y es a partir de ahí donde empezaremos a crecer el stack del proceso.

Como un buen ingenierio siempre lo menciona, hay que verificar lo que nos dan, más si es un puntero. Recordemos los límites de memoria del proceso:

  • Insertar imagen*

Como primer paso, debes de asegurarte que la dirección que generó el page fault sea una dirección válida, es decir, que la dirección que hizo el page fault se encuentre dentro de los límites permitidos. Si el fault address se encuentra en la memoria del Kernel... ¡inmediatamente matas el proceso! Si no, podemos seguir adelante pero asegúrate que las siguientes líneas de código no se ejecuten:

printf ("Page fault at %p: %s error %s page in %s context.\n",
          fault_addr,
          not_present ? "not present" : "rights violation",
          write ? "writing" : "reading",
          user ? "user" : "kernel");
kill (f);          // Esta línea la usamos para matar al proceso

Este proceso de Sanity Check lo puedes hacer directamente en syscall.c o en exception.c. Queda a tu discreción. La sugerencia es hacerlo en syscall.c porque si decides hacerlo en exception.c, presta mucha atención a la documentación de PintOS, porque deberás hacer unos pasos extras.

Puede ser que no necesites hacer esto si en la fase 2 del proyecto realizas los Sanity Checks cuando se manda a llamar algún Syscall.

Segunda Parte: Delimitar el crecimiento del Stack

Según la documentación de PintOS, debes de darle un límite al crecimiento del stack (8MB). Debes realizar los cálculos matemáticos para saber cuál es el límite inferior del Stack. Una vez lo tengas, debes de agregar una condición extra que verifique esto.

Tercera Parte: Crecimiento del Stack

Una vez terminamos con los pasos anteriores, estamos listos para implementar la primera funcionalidad de esta fase.

Deberás prestarle mucha atención a la variable fault_address, ya que contiene la dirección que causó el page_fault. Después de decidir si esa dirección es una dirección válida y pertenece al stack del proceso, procedes a crecer el stack del proceso.

OJO: debes tomar en cuenta las instrucciones PUSH y PUSHA que la guía menciona. Sí afectan las pruebas.

¿Cómo hacer crecer el Stack?

Cuando usas la función palloc_get_page(), este te devuelve la dirección de un frame disponible, pero cuando ya no existe un frame disponible, este te devuelve un Null. Sin embargo, esta función por sí sola no mantiene un registro de cuáles frames se han usado, solamente le importa devolverte el frame que se encuentre disponible.

Tu primer trabajo es implementar una función que realice lo mismo que palloc_get_page() pero que logre crear un FTE y un SPTE para mantener registro de quién le pertenece ese frame y registrar información adicional.

Te dejo un esquema (en pseudocodigo) de lo que tienes que hacer:

function(args){
    dir = palloc_get_page();
    make_fte();
    make_spte();
}

La implementación puede variar, pero procura que mínimo haga lo anteriormente descrito.

Una vez termines de implementar esa función, es momento de usarla cuando se genere un page_fault.

FAQ

  • ¿En dónde hago las funciones?

    Eso debes descubrirlo tú.

  • ¿Solamente eso debe hacer la función?

    Es lo básico; le darás tu toque y no quedará igual.

  • ¿Cómo valido lo de las instrucciones PUSH y PUSHA?

    En la documentación oficial lograrás encontrar esa respuesta.

  • ¿Puedo crecer el Stack cuando el page fault se genera en modo kernel?

    No. Matas al proceso. Solo puedes permitir un crecimiento en el stack si el page fault se generó en modo user. Ahora, si únicamente dependes de page_fault() para validar las direcciones, has caso omiso a esta pregunta.

  • ¿Mato inmediatamente al proceso si intenta escribir una página read-only?

    Sí.

Make Grade

Si lograste implementar bien la función anterior y realizaste correctamente el proceso de Sanity Check, al correr las pruebas deberías pasar las siguientes de manera exitosa:

Functionality

Robustness

Última actualización