1. Para la primera parte realizamos un pequeño código que prueba la manera como funcionan los candados.
Lock *L = new Lock("testLock"); void printFunc(void* name) { int i = 0; L->Acquire(); for(i; i<=5; i++) { printf("[%s] Testing Locks - No. [%d]\n", (char *)name, i); } L->Release(); } void ThreadTest() { DEBUG('t', "Testing the lock and another things"); printf ("TESTING:\n\n"); const char* name; Thread* t1 = new Thread("Cecy"); name = t1->getName(); t1->Fork(printFunc, (void *)name); Thread* t2 = new Thread("Roberto"); name = t2->getName(); t2->Fork(printFunc, (void *)name); Thread* t3 = new Thread("Juan"); name = t3->getName(); t3->Fork(printFunc, (void *)name); }EJECUCIÓN 2. Para nuestra primera presentación práctica decimos dar solución al problema de productor
A) Solución usando candados:
Lock *L = new Lock("testLock"); int buffer = 0; void bufferState() { printf("Buffer = [ %d ]\n", buffer); } void producer(void* name) { while(true) { L->Acquire(); printf("[%s] producing data\n", (char *)name); buffer++; bufferState(); L->Release(); currentThread->Yield(); } } void consumer(void* name) { while(true) { L->Acquire(); while(buffer == 0) { L->Release(); currentThread->Yield(); } printf("[%s] consuming data\n", (char *)name); buffer--; bufferState(); L->Release(); } } void ThreadTest() { DEBUG('t', "Probando los candados y otras cosas"); printf ("Producer-Consumer:\n\n"); const char* name; Thread* t1 = new Thread("Cecy"); name = t1->getName(); t1->Fork(producer, (void *)name); Thread* t3 = new Thread("Juan"); name = t3->getName(); t3->Fork(producer, (void *)name); Thread* t2 = new Thread("Roberto"); name = t2->getName(); t2->Fork(consumer, (void *)name); }EJECUCIÓN B) Solución usando semáforos
Semaphore* s1 = new Semaphore("cecy", 1); Semaphore* s2 = new Semaphore("robert", 0); int buffer = 0; void bufferState() { printf("Buffer = [ %d ]\n", buffer); } void producer(void* name) { while(true) { s1->P(); printf("[%s] producing data [S]\n", (char *)name); buffer++; bufferState(); if(buffer == 1) { s2->V(); } s1->V(); currentThread->Yield(); } } void consumer(void* name) { int temp; s2->P(); while(true) { s1->P(); printf("[%s] consuming data. [S]\n", (char *)name); buffer--; temp = buffer; bufferState(); s1->V(); if(temp == 0){ s2->P(); } } } void ThreadTest() { DEBUG('t', "Probando los candados y otras cosas"); printf ("Productor-Consumidor con semaforos:\n\n"); const char* name; Thread* t1 = new Thread("Cecy"); name = t1->getName(); t1->Fork(producer, (void *)name); Thread* t2 = new Thread("Juan"); name = t2->getName(); t2->Fork(producer, (void *)name); Thread* t3 = new Thread("Roberto"); name = t3->getName(); t3->Fork(consumer, (void *)name); }EJECUCIÓN C) Solución usando Variables Condicionales
Lock* l1 = new Lock("l1"); Condition* c = new Condition("cecy", l1); int buffer; int limite = 10; void bufferState() { printf("Buffer = [ %d ]\n", buffer); } void anadir() { l1->Acquire(); while(buffer == limite) { c->Wait(); } buffer++; bufferState(); c->Signal(); l1->Release(); } void extraer() { l1->Acquire(); while(buffer == 0) { c->Wait(); } buffer--; bufferState(); c->Signal(); l1->Release(); } void producer(void* name) { while(true) { anadir(); printf("[%s] producing data [CV]\n", (char *)name); } } void consumer(void* name) { while(true) { extraer(); printf("[%s] consuming data [CV]\n", (char *)name); } } void ThreadTest() { DEBUG('t', "Probando los candados y otras cosas"); printf ("Productor-Consumidor con variables de condicion:\n\n"); const char* name; Thread* t1 = new Thread("Roberto"); name = t1->getName(); t1->Fork(producer, (void *)name); Thread* t2 = new Thread("Juan"); name = t2->getName(); t2->Fork(producer, (void *)name); Thread* t3 = new Thread("Cecy"); name = t3->getName(); t3->Fork(consumer, (void *)name); }EJECUCIÓN REFERENCIAS:
Nos basamos más que nada en los pseudocódigos vistos en las notas y en los libros de Stalling, Tanenbaum, y el de los dinosaurios :)
en la primera parte en la ejecución, seria bueno tratar de llamar dos veces Acquire(), porque en realidad no se estan probando de manera optima la ejecución, porque como no hay scheduler, el FCFS se los imprime sin problemas.
ResponderEliminarEn su opinion cual fue la solucionl mas dificil de implementar en nachos,?
ResponderEliminarIndica a cuál de las soluciones te refieres Ever, Es una solución con Locks, posiblemente usemos Scheduler un poco más adelante.
ResponderEliminarSALUDOS :)
Erick: Jajaja, nos tomo más tiempo terminar la solución con Variables de Condición, pero no por la dificultad, ya eran como las 2 de la mañana y nuestro cerebro estaba medio dormido :P
Tengo la duda como agregaron locks y condition variables al NachOS, o ya venian incluidos?, otra cosa al parecer tampoco los que usan C++ estan corriendo programas de usuario como se debe :( esto lo interpreto ya que al terminar NachOS la linea que dice Ticks muestra O ticks de user lo cual indica que no hubo ninguna intervencion de usuario.
ResponderEliminarHola David, pusimos pseudocodigos de como implementamos locks y conditional variables al nachOS usando semáforos, no no venian implementadas, nosotros las implementamos.
ResponderEliminarSeria mucho problema poner los codigos? Es que en nuestra version de java ya venian implementados y pues tengo curiosidad de como los fueron desarrollando :)
ResponderEliminarJuan Carlos gracias por la informacion pero como te dijo Cecillia en mi blog nosotros lo hicimos en java no en c,igual y volveremos a intentar meter el codigo a nachos y cuando lo tengamos listo lo subiremos al blog
ResponderEliminarBueno carlos, a lo que me refería es que en la primera y segunda imagen se supone están haciendo las impresiones de pantalla atómicas. Pero por culpa del scheduler FCFS no se nota la implementacion. En mi versión tampoco esta implementado el sceduler, pero si usas varios fork y también usas el main, se logra hacer algo de variaciones entre los threads y de esa manera se lograra apreciar de verdad la seguridad de los locks :)
ResponderEliminarDavid: el código lo acabo de poner al final de la entrada anterior: Primera Presentación Teórica.
ResponderEliminarCarlos: Si, disculpa, confundí al leer el comentario de Obed, luego ya recorde... fe de erratas.
Everardo: Creo que el Scheduler ya viene implementado... posiblemente en una forma muy trivial. Sé a que te refieres, los threads entran a una cola de espera por lo que el orden será siempre el mismo. Espero hacer una prueba (pero más en secreto :P) con algunos candados adicionales.
Gracias por subir el codigo :) asi que a eso se referia Elisa con lo de lock a partir de semaforos heheh interesante como incluyen las funciones de los semaforos, en la version de java solo las condition variables estan relacionadas con los semaforos.
ResponderEliminarA mi me gustaria ver un scheduler modificado (intentare hacerlo en mis ratos libres), para ver un poco mas de "paralelismo".
ResponderEliminarMuy ilustrativas las imagenes y yo también quiero ver su código de variables condicionales =).
ya estan los codigos en la entrada teórica @David, @Jorge
ResponderEliminarCapturas de pantalla :( Para poder discutir bien, necesitan ponerles nombres "Código 1" etc e incluir números de línea.
ResponderEliminarDe esta discusión no pongo puntos extra a nadie; no veo que haya aportaciones relevantes nuevas.
Algo relevante, creo, sobre los códigos de lock y condition variable que mostraron es que seria buen ejercicio que los mejoraran para que no dependan de los semáforos, agregando por ejemplo las interrupciones necesarias esto para dar mas atomicidad a su implementacion :)
ResponderEliminar