Alarm Clock
Last updated
Last updated
En esta primera tarea, usted debe prestar atención a los siguientes archivos:
pintos/src/devices/
timer.h
timer.c
pintos/src/threads/
thread.h
thread.c
Ahora que ya sabemos en que directorio se encuentra cada archivo, por comodidad, nos limitaremos a llamar cada archivo solo por su nombre, usted ya sabe en que directorio se encuentra cada uno de estos.
La funcionalidad de timing, puede encontrarse en timer.h y timer.c. Veamos algunas funciones importantes que se encuentran en estos archivos:
int64_t timer_interrupt (struct intr_frame*args UNUSED); Es el reloj de Pintos. Cuando esta función sea llamada, la variable global ticks se incrementará en uno.
int64_t timer_elapsed (int64_t); Devuelve el numero de ticks que han transcurrido desde que Pintos inició su ejecución.
void timer_sleep(int64_t ticks); Suspende la ejecución del thread actual hasta que hayan transcurrido al menos el número de ticks recibidos como argumento.
La primera tarea es reimplementar timer_sleep. Veamos la implementación que se tiene inicialmente de timer_sleep:
Esto es conocido como "busy waiting". Lo que sucede en esta versión es que, existe un loop que revisa de forma iterativa el tiempo transcurrido y llama a thread_yield() hasta que hayan transcurrido el número de ticks recibidos como argumento.
De forma más detallada, esta implementación inicial, revisa si un thread debe estar durmiendo, si sí se supone que lo está, este cede su lugar a otro thread (yield) y se calendariza un nuevo thread al procesador. Pero thread_yield coloca al thread actual en ejecución, en un ready state de forma que no se garantiza que threads que estén dormidos sean calendarizados de nuevo al procesador antes que sea su hora de despertar, por lo que se desperdicia tiempo del procesador en tenerlo switcheando threads dormidos fuera y dentro de él.
Siga los siguientes consejos, y logrará implementar la funcionalidad de Alarm Clock correctamente, logrando así sus primeros 24 puntos aproximadamente en esta primera fase.
La idea general que seguiremos para deshacernos del "busy waiting" es bloquear a los threads, de forma que ya no se encuentren en un ready state y cuando sea su hora de despertar los desbloqueamos.
Para ello, haga lo siguiente:
Defina en thread.h un nuevo atributo en la estructura thread, que sea un entero que represente el tiempo que un thread debe permanecer dormido.
Defina en thread.c una estructura que sea una lista de los threads que están en espera de que se cumpla su tiempo de estar durmiendo.
Inicialice la lista anterior en thread_init, supongamos que en el paso anterior le llamó a esta "lista_espera" .
Deshágase del problema inicial removiendo el ciclo y llamando a una función que usted definirá en thread.c, llamémosle insertar_en_lista_espera, que tome al thread actual, lo bloquee y lo inserte en la lista de espera que definimos en el paso anterior.
Quizás quiera agregar la definición de esta nueva función en thread.h antes de implementarla en thread.c.
Implemente insertar_en_lista_espera, esta se vería de la siguiente forma:
Ahora que aseguramos que los threads estan bloqueados en esa lista, y que conocemos cuantos ticks deben permanecer en ella, debemos desbloquearlos cuando se haya cumplido su tiempo de expiración, para ello regresamos a timer.c y en timer_interrupt podemos llamar a otra función que usted definirá nuevamente en thread.c la cual llamaremos ahora, remover_thread_durmiente.
Quizás quiera agregar la definición de esta nueva función en thread.h antes de implementarla en thread.c.
Y remover_thread_durmiente se vería de la siguiente forma.
Si usted siguió todos los pasos anteriores de forma correcta, puede ejecutar el comando make grade en la ubicación pintos/src/threads debería pasar algunos tests, equivalentes aproximadamente a 24/100 puntos de esta fase.
Por lo que ha realizado con éxito la primera tarea de la primera fase de Pintos.
Es probable que en las siguientes tareas, usted tenga que modificar o agregar cosas en las funciones que modificamos o agregamos en esta primera tarea, por lo que tome en cuenta que estarán sujetas a cambios.
Esta tarea prácticamente es un empujón, que le sirve a usted para adentrarse en este proyecto y aprobar los primeros tests. Su propósito era que trabajara y entendiera unas cuantas estructuras como lo son los threads y listas, y alguna de las cosas que logró hacer fueron las siguientes:
Manipulación de threads.
Añadir atributos a esta estructura.
Utilizar las funciones definidas en thread.c (thread_block, thread_unblock, thread_current).
Manipulación de listas.
Crear e inicializar una lista.
Insertar elementos.
Remover elementos.
Recorrer la lista.
Tomar un elemento de la lista, instanciarlo y manipularlo.
Habilitar y deshabilitar interrupciones.
Esto le será de utilidad ya que son acciones que realizará de manera muy frecuente a lo largo de todo el proyecto, o se topará con cosas a implementar que serán muy parecidas y sigan esta misma lógica.
Además considero que esta tarea guiada le servirá para perderle el miedo a modificar archivos, tratar de implementar cosas, y desarrollar ideas que usted pueda tener. Se le invita a intentar hacer lo realizado de otra forma, quién sabe, ¿No crear nuevas funciones en thread.c?, ¿Utilizar otra estructura distinta a una lista?, ¿Hacerlo todo en timer.c?, etc... esas son solo ideas, no significa que funcione... esto queda a su imaginación...
NOTA: Tome en cuenta que, en las siguientes tareas de las distintas fases del proyecto, ya no será todo tan detallado ni específico, se proporcionarán hints, mas sin embargo no será la solución exacta como se presentó en esta tarea de esta fase.