PRÁCTICAS DE LABORATORIO DE SISTEMAS OPERATIVOS

SEGUNDA PRÁCTICA OBLIGATORIA (2002-03)

Parole


  1. Enunciado.

    El programa propuesto constará de un único fichero fuente, parole.c, cuya adecuada compilación producirá el ejecutable parole. En esencia, el programa producirá palabras de cuatro letras durante 30s siguiendo el procedimiento y las restricciones que a continuación se detallan.

    Parole acepta un único argumento de la línea de órdenes. Es un número entero mayor o igual que cero relacionado con la rapidez con que se producen los acontecimientos en el programa. Cero indica la máxima rapidez y números mayores suponen una rapidez progresivamente menor.

    Para poder realizar la práctica debéis usar únicamente cuatro secursos IPC: un conjunto de semáforos, dos buzones, uno de usuario y otro para la biblioteca, y una zona de memoria compartida.

    Para facilitar la tarea, tenéis a vuestra disposición una biblioteca de funciones libparole.a. Gracias a ella, muchas de las funciones no las tendréis que programar sino que nada más tenéis que incluir la biblioteca cuando compiléis el programa. La línea de compilación del programa podría ser:
    c89 parole.c libparole.a -o parole
    La biblioteca creará algún proceso adicional a los vuestros para su funcionamiento interno. Estos procesos también tenéis que eliminarlos al finalizar el programa. Una descripción detallada de la biblioteca aparece más abajo en esta misma página.

    El programa, desde vuestro punto de vista, consta de tres procesos:
    1. Proceso P: es el proceso padre de todos. Registra sus señales, crea los recursos IPC y les da valores iniciales, crea dos tuberías sin nombre y llama a la función inicio_parole(...) de libparole. Por una de las tuberías, libparole le enviará consonantes y, por la otra, vocales. Después de llamar a inicio_parole(...), P crea a sus dos procesos hijos, C y V. Hasta que transcurran los 30 segundos, P leerá de las tuberías. No debe dar preferencia a ninguna de ellas. Si acaba de leer de la tubería de las consonantes deberá:
      1. Mandar un mensaje de tipo IR_A_POR_CONSONANTE al buzón de usuario. Se debe usar una variable de tipo struct mensaje para enviar los mensajes. Tanto la macro IR_A_POR_CONSONANTE como la estructura struct mensaje se encuentran definidas en el fichero de cabecera parole.h. Dentro del mensaje enviado, en el campo letra debe ir la letra recibida por la tubería.
      2. P debe añadir la letra leída a la cola de letras de su tipo, en este caso, a la cola de consonantes. Pero antes de hacerlo debe cerciorarse de que haya sitio en la cola. La cola es de tamaño limitado (12 letras). Si no hay sitio en la cola, P se debe esperar sin consumir CPU a que haya sitio. Una vez haya sitio, enviará al buzón de usuario un mensaje de tipo ANADIR_CONSONANTE con la letra leída de la tubería. Libparole se encargará de gestionar todo y de añadir la consonante a la cola. Vuestro programa no tiene que hacer nada más que mandar el mensaje.
      Si P lee una vocal, realizará lo mismo que se ha explicado para las consonantes pero con las estructuras de las vocales.
    2. Proceso C: es hijo de P. Y está muy orgulloso de ello. Al nacer, ajusta las señales por si quiere un comportamiento diferente al heredado de su padre. Su vida transcurre en un bucle infinito, dentro del cual realiza lo siguiente:
      1. Si hay consonantes en la cola de consonantes, llamará a la función tomar_letra de libparole para obtener la primera letra de la cola.
      2. Entre él y su hermano V se tienen que sincronizar para componer una palabra de cuatro letras, sujeta a la siguiente ley: "La palabra seguirá el patrón: consonante-vocal-consonante-vocal o el patrón: vocal-consonante-vocal-consonante. Ninguna otra palabra será válida".
      3. C luchará con V para ver si la palabra que van a formar será del primer o del segundo tipo. El que primero escriba la primera letra decidirá el tipo de palabra en esa ronda. Una vez establecido, los procesos se han de sincronizar para que la escritura se realice en orden: no se puede escribir una letra sin haber escrito las anteriores.
      4. La palabra que se está formando reside en los cuatro primeros bytes de la zona de memoria compartida. Para escribir una letra, C ó V sólo tienen que escribir en la zona de memoria compartida. Para que lo escrito en la zona aparezca por la pantalla, el proceso, después de haber escrito, debe llamar a la función imprimir_palabra(...) de libparole.
      5. El proceso que haya escrito la última letra de la palabra debe llamar a la función validar_palabra de libparole para que la biblioteca compruebe que la palabra formada es correcta.
    3. Proceso V: su funcionamiento es el simétrico de C.


    Características adicionales que programar



    Biblioteca de funciones libparole

    Con esta práctica se trata de que aprendáis a sincronizar y comunicar procesos en UNIX. Su objetivo no es la programación. Es por ello que se os suministra una biblioteca estática de funciones ya programadas para tratar de que no tengáis que preocuparos por la presentación por pantalla, la gestión de estructuras de datos (colas, pilas, ...) , etc. También servirá para que se detecten de un modo automático errores que se produzcan en vuestro código. Para que vuestro programa funcione, necesitáis la propia biblioteca libparole.a y el fichero de cabecera parole.h. La biblioteca funciona con los códigos de VT100, por lo que debéis adecuar vuestros simuladores a este terminal.
    Ficheros necesarios:


    Las funciones que podéis usar de la biblioteca son:

  2. Pasos recomendados para la realización de la práctica

    Aunque ya deberíais ser capaces de abordar la práctica sin ayuda, aquí van unas guías generales:
    1. Crear los semáforos, la memoria comparida, las tuberías sin nombre, y comprobar que se crean bien.
    2. Registrar SIGINT para que cuando se pulse ^C se eliminen los recursos IPC. Lograr que si el programa acaba normalmente, también se eliminen los recursos.
    3. Llamar a la función inicio_parole en main.
    4. Hacer el select en el programa principal y que nos lea de las tuberías apropiadas y distinga entre vocales y consonantes. Ir en libparole a por las letras leídas mediante el buzón de usuario.
    5. Añadir con libparole la letra leída. Cuando se sobrepase el límite de la cola de un tipo, aparecerá un error: cola llena.
    6. Poner los medios para evitar el error. El proceso se tiene que quedar bloqueado cuando no haya sitio en la cola...
    7. Dejar al proceso principal en pause(); temporalmente. Crear los procesos C y V que están continuamente solicitando a libparole letras de su correspondiente cola. Como la cola está vacía, pondrá error. Solucionar dicho error.
    8. Antes de probar el programa en su conjunto, probar con varias combinaciones de procesos: sólo con P, sólo con C, sólo con V, sólo con dos de ellos y ver si lo que sale es lógico.
    9. Ahora es el momento de probar el programa a la máxima velocidad y con todos los procesos. Si no funciona, de nuevo reducid el número de procesos temporalmente y tratad de diagnosticar el error.
    10. Añadir las funcionalidades adicionales: recuento de palabras, límite de 30 segundos...


  3. Plazo de presentación.

    Hasta el lunes 19 de mayo de 2003, inclusive.

  4. Normas de presentación.

    Acá están. Además de estas normas, en esta práctica se debe entregar un esquema donde aparezcan los semáforos usados, sus valores iniciales y un seudocódigo sencillo para cada proceso con las operaciones wait y signal realizadas sobre ellos. Por ejemplo, si se tratara de sincronizar dos procesos C y V para que produjeran alternativamente consonantes y vocales, comenzando por una consonante, deberíais entregar algo parecido a esto:
         SEMÁFOROS Y VALOR INICIAL: SC=1, SV=0.
    
         SEUDOCÓDIGO:
    
                 C                                V
                ===                              ===
           Por_siempre_jamás               Por _siempre_jamás
              {                               {
               W(SC)                           W(SV)
               escribir_consonante             escribir_vocal
               S(SV)                           S(SC)
               }                               }
    


  5. Evaluación de la práctica.

    Dada la dificultad para la corrección de programación en paralelo, el criterio que se seguirá para la evaluación de la práctica será: si
    1. la práctica cumple las especificaciones de este enunciado y,
    2. la práctica no falla en ninguna de las ejecuciones a las que se somete y,
    3. no se descubre en la práctica ningún fallo de construcción que pudiera hacerla fallar, por muy remota que sea esa posibilidad...
    se aplicará el principio de "presunción de inocencia" y la práctica estará aprobada. La nota, a partir de ahí, dependerá de la simplicidad de las técnicas de sincronización usadas, del número de palabras que se produzcan a máxima velocidad, etc.

  6. LPEs.

    1. ¿Dónde poner un semáforo? Dondequiera que uséis la frase, "el proceso debe esperar hasta que..." es un buen candidato a que aparezca una operación wait sobre un semáforo. Tenéis que plantearos a continuación qué proceso hará signal sobre ese presunto semáforo y cuál será su valor inicial.
    2. ¿Cómo hacer para que la formación de la palabra funcione y se respete los patrones CVCV y VCVC? No existe una única solución. Lo más sencillo es, desde mi punto de vista, que creéis dos esquemas: uno que produzca CVCV, el esquema I, y otro que produzca VCVC, el esquema II. Antes de imprimir la primera letra de una palabra, diseñad un mecanismo para que el proceso sepa si todavía no hay esquema decidido e impone el suyo o si el otro proceso se adelantó e impuso su esquema. En cualquier caso, el proceso ejecutará esquema I o esquema II según corresponda. Y cuidado con la concurrencia en el esquema de decisión que uséis.
    3. Si al hacer los esquemas resulta que os funciona con la primera palabra, pero no cuando se van a hacer más palabras, mirad a ver si ocurre lo siguiente: suponiendo que el esquema anterior fuera CVCV, y que el proceso C ha escrito ya su segunda consonante, puede ser que antes de que el proceso V acabe y valide la palabra, el proceso C ya esté imponiendo el esquema de la siguiente y sobreescriba la primera letra. El proceso C debería esperarse a que el proceso V acabara y validara la palabra (frase muy candidata a hacer que aparezca un semáforo en el código, ¿no?)


  7. Prácticas propuestas años anteriores.


© 2003 Guillermo González Talaván.