PRÁCTICAS DE LABORATORIO DE SISTEMAS OPERATIVOS

DECIMOCUARTA SESIÓN


  1. Memoria dinámica en Windows NT.

    La biblioteca estándar de C, libc, proporciona las funciones malloc, calloc y free para el manejo de la memoria dinámica.

    Estas funciones manejan una zona de la memoria del programa conocida como montón del proceso (process heap). Existe la posibilidad de manejar el montón del proceso directamente o crear otros montones de memoria dinámica de usuario en Windows NT. Son también funciones de biblioteca y todas comienzan por Heap...

    Para crear un montón de usuario propio, hay que invocar:
    HANDLE HeapCreate( DWORD flOptions, DWORD dwInitialSize, DWORD dwMaximumSize); 
    Su uso viene explicado en el resumen. Simplemente resaltar que si se especifica un tamaño máximo de 0, se permite al montón crecer indefinidamente y se permite reservar memoria de ese montón sin límite de tamaño. Si no, el límite de cada reserva es de 0x7FFF8 bytes.

    Una vez creado el montón se puede ir pidiendo zonas de memoria dinámica con HeapAlloc. Existen también funciones para liberar memoria y para destruir un montón creado.

    Hay que tener en cuenta que si hay varios hilos accediendo a un mismo montón a la vez se pueden producir problemas de concurrencia y dejar el montón corrupto. El acceso al montón constituye una sección crítica del proceso. Por defecto, el propio sistema de gestión del montón se encarga de arbitrar el acceso al montón. Sin embargo, si especificamos la opción HEAP_NO_SERIALIZE en CreateHeap o similares, nos tendremos que encargar nosotros. Esto puede llegar a interesar si las necesidades de rapidez son altas.

  2. Reserva de memoria virtual.

    Las verdaderas llamadas al API para la reserva de memoria dinámicamente en un proceso de Windows NT son aquellas que comienzan por Virtual...

    Para poder obtener memoria, primero hay que solicitar la reserva, luego hay que confirmarla (COMMIT). La reserva sólo se producirá en múltiplos del tamaño de página del sistema. Para reservar o confirmar memoria virtual se usa la función:
    LPVOID VirtualAlloc( LPVOID lpAddress, DWORD dwSize, DWORD flAllocationType, 
                         DWORD flProtect); 
    Reservaremos, si ponemos MEM_RESERVE en los flAllocationType. Confirmaremos con MEM_COMMIT. Respecto al primer parámetro, puede ser NULL si, cuando hacemos la reserva (no cuando confirmamos) dejamos al sistema que elija la dirección virtual donde reservar la memoria.

    Una zona reservada no está gastando memoria real, sólo tenemos reservado el rango de direcciones. Sólo cuando confirmamos, es cuando realmente se pone memoria real detrás. En el resumen vienen más indicadores y su significado.

    La memoria, cuando se acabe de usar, debe ser liberada, es decir pasar a no confirmada, y las direcciones de memoria liberadas. Se hará con VirtualFree. También es posible bloquear una zona de memoria de modo que no se haga paginación con ella y permanezca en memoria principal a ser posible. Esto se consigue con VirtualLock y VirtualUnlock. Debido a que las prestaciones del sistema pueden verse muy mermadas si se bloquea mucha memoria, el sistema operativo sólo dejará bloquear pocas páginas.

    Se dispone, además, de la función GlobalMemoryStatus que nos dará información de la memoria disponible en el sistema.

  3. Memoria compartida y ficheros proyectados en memoria.

    Para Windows NT es lo mismo la memoria compartida entre procesos y los ficheros proyectados en memoria. En UNIX, vimos lo primero en la sesión segunda y lo segundo, en la octava. Como consecuencia de esta fusión, los ficheros proyectados en memoria tendrán, como los semáforos de Windows NT la posibilidad de tener o no un nombre para que los puedan usar otros procesos. Dispondremos, pues, de dos funciones, CreateFileMapping y OpenFileMapping, como con los semáforos. Veamos, por ejemplo, la primera:
    HANDLE CreateFileMapping(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
                             DWORD flProtect, DWORD dwMaximumSizeHigh, 
                             DWORD dwMaximumSizeLow, LPCTSTR lpName);  
    hFile tiene que ser:

    Como puede ser que el tamaño de un fichero sea superior a lo que cabe en 32 bits (variable de tipo DWORD), el tamaño se especifica de dos veces. Es decir, se construyen dos valores de 32 bits a partir de la cantidad de 64 bits y se pasan como argumentos diferentes.

    El último parámetro nos va a dar el nombre de la zona de memoria por si queremos compartirla con otro proceso.

    LPVOID MapViewOfFile( HANDLE hFileMappingObject, DWORD dwDesiredAccess, 
                          DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, 
                          DWORD dwNumberOfBytesToMap); 
    servirá para proyectar efectivamente el fichero en la zona de memoria. El significado de los parámetros es evidente. Sólo considerar que, de nuevo, el desplazamiento dentro del fichero es de 64 bits repartidos en dos pedazos de 32 bits. La función nos devolverá la dirección de memoria donde se ha realizado la proyección. La función opuesta la realiza UnmapViewOfFile. También se dispone de la función FlushViewOfFile para hacer efectivo el volcado de la zona de memoria sobre el fichero.

  4. Primera práctica.

    El ecologismo está de moda, así que ahí va una práctica reciclada.

    Haremos un programa del estilo al del productor-consumidor que vimos en clase, con memoria compartida. Habrá dos procesos, A y B, el A será el proceso productor. El B, el proceso consumidor. Para comunicarse, usarán una zona de memoria compartida de un carácter. El proceso A producirá los caracteres: "En un lugar de la Mancha de cuyo nombre no quiero acordarme, no ha mucho que vivía un hidalgo de los de lanza en astillero, adarga antigua, rocín flaco y galgo corredor.". Producirá los caracteres a un ritmo de 10 caracteres por segundo (usad la llamada al API Sleep para controlar esto). El proceso B, por su parte, consumirá los caracteres según le vayan llegando, con un gasto de CPU mínimo. Para consumir un carácter, lo que hace es leerlo. A los caracteres que le llegan en una posición par (segundo, cuarto, etc.) los imprime por la pantalla tal cual. A los que le llegan en una posición impar, los transforma en mayúsculas y los imprime por pantalla. Para trasformar en mayúsculas usad la función de biblioteca toupper. Haced un único programa fuente (.c). Si es necesario, podéis usar un argumento de la línea de órdenes opcional.

  5. Segunda práctica.

    Haced un programa que admita un nombre de fichero como único parámetro. Se llamará el programa mataoes. El programa, mediante proyección del fichero en memoria eliminará todas las oes minúsculas ('o') que tenga el fichero, posiblemente quedando la longitud de dicho fichero reducida.

  6. Aplicaciones relacionadas.



  7. Funciones de biblioteca relacionadas.

    HeapCreate, HeapAlloc, HeapSize, HeapRealloc, HeapFree
    funciones de manejo de los montones de memoria dinámica


  8. LPEs.


© 2000 Guillermo González Talaván.