PRÁCTICAS DE LABORATORIO DE SISTEMAS OPERATIVOS

SEGUNDA PRÁCTICA OBLIGATORIA (2001-02)

Gases


  1. Enunciado.

    La práctica constará de un solo programa ejecutable gas procedente de la compilación de un programa fuente en C, gas.c.

    El programa creará una tubería con nombre delfos y situada en el directorio desde donde se lance el programa. También declarará una zona privada de memoria compartida capaz de contener el array:
    char *campo="\
    XXXXXXXXXXXXXXXXXXX\n\
    X                 X\n\
    X                 X\n\
    X                 X\n\
    X                 X\n\
    X                 X\n\
    X                 X\n\
    X                 X\n\
    X                 X\n\
    X                 X\n\
    X                 X\n\
    XXXXXXXXXXXXXXXXXXX\n\
    ";
    El proceso padre realizará dos tareas de un modo simultáneo: En cuanto a los procesos bola, se moverán en diagonal con un intervalo de avance del doble que el intervalo de refresco de pantalla. Comenzarán moviéndose hacia el SE (sudeste), pudiendo variar su rumbo dependiendo de si se encuentran con una pared (marcada con una X u otra bola, marcada con una O). El algoritmo es muy sencillo: Suponiendo (x,y) la posición de la bola y (vx,vy) su velocidad (que sólo puede tomar los valores (1,1):SE, (1,-1):NE, (-1,1):SW y (-1,-1):NW), el algoritmo es:
         dormir;
         choque=false;
         si (x+vx,y) está ocupado, vx=-vx y choque=true
         si (x,y+vy) está ocupado, vy=-vy y choque=true
         si no se ha producido choque hasta ahora y (x+vx,y+vy) está ocupado, 
             vx=-vx, vy=-vy y choque=true
         si no se ha producido choque, borrar la bola, x=x+vx e y=y+vy, pintar la bola
    El programa admitirá un parámetro en la línea de órdenes de tipo entero que indicará cuál será el intervalo entre refrescos de pantalla, expresado en décimas de segundo, con posibilidad de que se extienda más allá del segundo.

    Ningún proceso debe consumir CPU de un modo innecesario ni realizar esperas ocupadas ni semiocupadas. Para lograr que el programa duerma intervalos inferiores al segundo (e incluso pueda atender a la tubería en el caso del proceso padre, usad multiplexión de entrada/salida síncrona). Para conseguir parar a todas las bolas de golpe, es mejor usar las funciones de grupo de procesos que se vieron en la sesión novena.

    Aunque en el enunciado no se habla nada acerca de sincronización, pensad en que seguro que es necesario añadir alguna y que sin ella, la práctica está mal. Defectos de mala sincronización son la desaparición temporal de una bola, que las bolas se atraviesen sin chocar, etc. Considerad, no obstante, que el hecho de que no se produzcan estos efectos no significa que la práctica esté bien sincronizada.

    Cuando el programa finalice, sea por el motivo que sea, debe limpiar los recursos utilizados, tanto los recursos IPC como la tubería con nombre.

  2. Plazo de presentación.

    Hasta el lunes 20 de mayo de 2001, inclusive.

  3. Normas de presentación.

    Acá están.

  4. LPEs.

    1. Puede ocurrir que cuando probéis el programa en Linux o en encina os aparezca el mensaje de error:
      <ENCINA>/home/gyermo$ gas 2
      Assembler messages:
      Error: Can't open 2 for reading.
      2: No existe tal archivo o directorio
      
      Investigando un poco en el error descubrimos que:
      <ENCINA>/home/gyermo$ which gas
      /opt/sfw/bin/gas
      
      O, lo que es lo mismo, el ordenador no ejecuta nuestro programa sino otro suyo propio situado en el directorio /opt/sfw/bin. Dicho programa es el ensamblador de GNU (GNU Assembler). Para solucionar el problema, podéis especificar todo el camino de acceso al ejecutable desde el directorio raíz o alternativamente teclear ./gas.
    2. Ningún proceso debe abrir la tubería delfos para lectura y escritura (O_RDWR). El padre la debe abrir para sólo lectura, pues únicamente desea leer de ella. Si tenéis problema con esto, quizá deberíais repasar la sincronización de apertura de tuberías en la sesión octava.
    3. Muchos de vosotros se queja de que el acceso al array campo es un tanto difícil. Dada la posición (x,y) de una bola, para acceder al array campo y considerando la esquina superior izquierda donde puede estar una bola como la posición (1,1), para escribir una 'O' en esa posición, haríamos:
      campo[x+20*y]='O';
      lo que tampoco es tan complicado. Algunas personas me han comentado si no sería mejor definir un array bidimensional. El problema de esto es que en el refresco, se va refrescando línea a línea y el efecto no es muy bueno. No obstante, para los muy exquisitos, C ofrece algunas soluciones:
      1. Encapsular la escritura del array en una función:
        int pos(int x, int y) {return x+20*y;}
        ...
        campo[pos(x,y)]='O';
      2. Para que no se penalice con la llamada a función cada vez que se accede al array, definir una macro:
        #define POS(x,y) (x+20*y)
        ...
        campo[POS(x,y)]='O';
        Pero si estamos por optim(iz)ar el tiempo (lo que es un poco absurdo en este problema), quizá podamos hacer la multiplicación por 20 como sumas:
        #define POS(x,y) (x+y<<2+y<<4)
        ...
      3. Podemos aprovechar la equivalencia entre punteros y arrays de C:
        char *nuevo_campo[12];
        ...
        for (int i=0; i<sizeof(nuevo_campo); i++)
           {nuevo_campo[i]=campo+20*i;}
        ...
        nuevo_campo[y][x]='O';
        Se accedería a través de nuevo_campo y se refrescaría directamente con campo.
      Pero yo no veo tanta ventaja en los esquemas que he puesto, conste.


  5. Prácticas propuestas años anteriores.


© 2002 Guillermo González Talaván.