¿QUÉ ES MEMORIA VIRTUAL?
==========================
A medida que los programas de aplicación se han hecho más sofisticados y
más grandes, se ha necesitado de ampliar las posibilidades de la memoria
dinámica de los sistemas. Debido a lo poco económico que resulta atender
la gran exigencia de memoria simplemente implementando ships con elevada
capacidad de almacenamiento, este problema se ha abordado vía software,
a través de sistemas operativos que ofrecen gestión de memoria virtual.
Es un mecanismo de gestión de la memoria de un sistema que
implementa un espacio de memoria ficticio, llamado espacio de direcciones
o de nombres, el cual es mucho mayor que el espacio de memoria real física
que posee el hardware del sistema. De esta manera, el programador al desarrollar
aplicaciones para un SO con memoria virtual, no tiene que ocuparse de la
memoria física, simplemente programa como si tuviera a su disposición una
gran espacio de direcciones para cada proceso.
¿CÓMO ES POSIBLE LA GESTIÓN DE MEMORIA VIRTUAL?
=================================================
El programador desarrolla sobre un gran espacio de memoria virtual, compila
o ensambla sus fuentes para producir los ejecutables de sus aplicaciones.
Es todo lo que hace. El SO se encarga de lo demás: traduce las direcciones
virtuales en direcciones físicas y carga en la RAM del sistema sólo aquellos
bloques de la aplicación que necesi ta, nunca carga todo el ejecutable ya
que éste seguramente ocuparía todo el espacio de memoria física disponible
y el sistema colapsaría.
¿CÓMO TRADUCE W32 DIRECCIONES VIRTUALES A REALES?
=====================================================
W32 requiere la presencia de un microprocesador Intel 386, 486 o Pentium.
Estos procesadores usan direccionamiento de memoria de 32 bits y por tanto
son capaces de acceder 2 elevado a la 32, es decir, 4.294.967.296 bytes
(4 gigabytes o GB) de memoria física. Por supuesto, la mayoría de los usuarios
de W32 no están cerca de ese límite.
Las direcciones de 32 bits usadas por los programas W32 para
acceder al código y los datos no son las direcciones físicas de 32 bits
que el microprocesador usa para direccionar la memoria física. La dirección
que utiliza la aplicación se llama dirección «virtual» la cual es una dirección
ficticia que se traduce a una dirección física a través de una «tabla de
página».
Para leer una instrucción o dato en la RAM, el CPU necesita
colocar en el BUS de direcciones esa dirección donde debe estar ubicada
la dirección o dato solicitado. Por ejemplo, si quiere acceder a la memoria
de video, que está en 0A0000h, el CPU debe colocar este valor en el BUS.
Como las direciones de las aplicaciones son direcciones virtuales,
el CPU debe traducirlas a direcciones físicas antes de acceder a los datos
en la RAM.
Las aplicaciones suelen ignorar este proceso. El programa
parece almacenarse en un espacio de direcciones de 32 bits y no hay nada
extraño cuando se tiene que acceder a esta memoria. Sin embargo, es conveniente
hacerse una idea de lo que significa esto.
W32 pagina la memoria física, la divide en «páginas» con una
longitud de 4096 bytes (4 KB). Una máquina equipada con 8 megabytes de memoria
tiene 2048 páginas. W95/98 mantiene una colección de tablas de página (ellas
mismas páginas 4 KB) para traducir direcciones virtuales a direcciones físicas.
Cualquier proceso en W32 tiene su propia «página de directorio»,
que es una colección de hasta 1024 entradas de 32 bits almacenadas contiguamente,
una después de otra. La dirección física de la página de directorio activa
se almacena en un registro del microprocesador llamado CR3 (CONTROL REGISTER
3), que se cambia cuando el SO conmuta el control entre procesos. Los 10
bits altos de una dirección virtual especifican una de las 1024 entradas
posibles en esta página de directorio. Los 20 bits altos de la entrada de
la página de directorio indica una dirección física de una tabla de página
(los 12 bits inferiores de la dirección física se definen a cero). Esto
referencia otra página, que también tiene hasta 1024 entradas de 32 bits.
Los 10 bits del medio de la dirección virtual referencian una de estas entradas.
De nuevo, la entrada tiene una dirección física de 20 bits para indicar
la posición de comienzo de un marco de página, que es una dirección física.
Los 12 bits inferiores de una dirección virtual apuntan a una posición física
dentro de este marco de página.
Mostrado simbólicamente, uno puede representar una dirección
virtual de 32 bits (que es con lo que trabaja una aplicación) como una entrada
de página de directorio de 10 bits (d), una entrada de tabla de página de
10 bits (p) y un offset de 12 bits:
dddd-dddd-ddpp-pppp-pppp-oooo-oooo-oooo
Para cada proceso, el microprocesador almacena un valor de
20 bits en el registro CR3 (r de registro):
rrrr-rrrr-rrrr-rrrr-rrrr
La dirección física de comienzo de la página de directorio
activa del proceso es:
rrrr-rrrr-rrrr-rrrr-rrrr- 0000-0000-0000
Recuérdese que todas las páginas se almacenan en límites de
4 KB, por tanto, cada página comienza en una dirección con los 12 bits inferiores
igual a cero. El microprocesador primero accede a la dirección física:
rrrr-rrrr-rrrr-rrrr-rrrr-dddd-dddd-dd00
Esta posición contiene otro valor de 20 bits (t de tabla):
tttt-tttt-tttt-tttt-tttt
lo cual indica la dirección física de comienzo de una tabla
de página:
tttt-tttt-tttt-tttt-tttt-0000-0000-0000
El microprocesador accede luego a una dirección física:
tttt-tttt-tttt-tttt-tttt-pppp-pppp-pp00
Almacenado en este área hay un valor de 20 bits de un marco
de página (c de marco):
CCCC-CCCC-CCCC-CCCC-CCCC
La dirección fisíca final de 32 bits es una combinación de
este marco de página con los 12 bits de offset inferiores de la dirección
virtual:
CCCC-CCCC-CCCC-CCCC-CCCC-0000-0000-0000
Esta es la dirección física. Es todo.
Puede parecer que traducir una dirección virtual en dirección
física es un proceso que lleva mucho tiempo, pero realmente no es así. Los
microprocesadores Intel 386, 486 y Pentium tienen una memoria cache interna
que puede guardar estas tablas de página dentro del microprocesador. La
traducción se realiza de forma muy rápida sin ninguna penalización de tiempo.
Esta doble paginación (páginas que se guardan en una página) ofrece a cada
aplicación un límite teórico de aproximadamente un millón de páginas de
4 KB.
EMPLEANDO SICE
================
Podemos revisar el proceso de traducción de memoria virtual a memoria física
con SICE.
Carguemos notepad.exe con el loader de SICE. Cuando se despliega
la pantalla de SICE, el depurador está apuntando a la primera instrucción
del programa cuya dirección virtual es 00401000h. A partir de esta dirección
podemos calcular la dirección física donde se ubica esta instrucción en
la RAM.
Traduzcamos el valor hexadecimal de la dirección virtual a
binario:
0000 0000 0100 0000 0001 0000 0000 0000
Tenemos:
1. Entrada del directorio de páginas o primario, diez bytes altos:
0000 0000 01 = 1
Es la entrada 1 del directorio primario
2. Entrada de la tabla de páinas o directorio secundario,
diez bytes medios:
00 0000 0001 = 1
Es la entrada 1 del directorio secundario
3. Desplazamiento en el marco de página de memoria física,
doce bytes bajos restantes:
0000 0000 0000 = 0
Comienzo del marco de página o desplazamiento cero. Recuérdese
que es la primera instrucción del programa.
Para determinar exactamente la dirección física de la dirección
debemos:
1. obtener la dirección física donde está el directorio de
páginas activas o directorio primario. Este valor está en CR3 y lo obtenemos
con el comando 'CPU' de SICE. Cada entrada de este directorio es un puntero
al directorio secundario.
2. obtener la dirección de la entrada en el directorio primario
donde se encuentra la dirección del comienzo del directorio secundario.
Sabemos la dirección del inicio del directorio primario por los 20 bits
altos del contenido de CR3. La entrada dentro del directorio primario que
dice donde está la tabla que interesa está indicada por los diez bits altos
de la dirección virtual. Como cada entrada del directorio primario es de
32 bits = 4 bytes, hay que multiplicar el número de entrada por 4 para obtener
la dirección de la entrada correspondiente en el directorio primario.
3. obtener la dirección física dónde se encuentra la tabla
de páginas o directorio secundario. Con la dirección de la entrada del directorio
de página donde está la dirección de la tabla de páginas activas, se puede
emplear el comando PEEK de SICE para obtener esta última dirección:
PEEK D DIRECCIÓN_DE_ENTRADA_DE_DIRECTORIO_PRIMARIO
4. obtener la dirección de la entrada en la tabla de páginas
donde está la dirección del inicio o marco de la página donde está la instrucción
apuntada por la dirección virtual. Sabemos la dirección del inicio del directorio
secundario por los 20 bits altos del contenido la entrada del directorio
primario. La entrada dentro del directorio primario que dice donde está
el mmraco de página que buscamos está indicada por los diez bits medios
de la dirección virtual. Como cada entrada del directorio secundario es
de 32 bits=4 bytes, hay que multiplicar el número de entrada por 4 para
obtener la dirección de la entrada correspondiente en el directorio secundario.
5. obtener la dirección física dónde se encuentra el marco
de página donde se ubica la instrucción señalada por la dirección virtual.
Con la dirección de la entrada del directorio secundario donde está la dirección
del marco de páginas, se puede emplear el comando PEEK de SICE para obtener
esta última dirección, ya que este comando devuelve el contenido de una
dirección física:
PEEK D DIRECCIÓN_DE_ENTRADA_DE_DIRECTORIO_SECUNDARIO
6. obtener la dirección física de la instrucción en la página
específica. Este valor se obtiene a partir de los 20 bits altos del contenido
en la entrada correspondiente en el directorio secundario y los 12 bits
bajos en la dirección virtual. Estos bits bajos de la dirección virtual
son un desplazamiento dentro del marco de página donde se ubicó el contenido
de la aplicación.
7. obtener el contenido en la dirección física señalada por
el desplazamiento dentro del marco de página donde se encuentra el código
o los datos indicados por la dirección virtual. Finalmente el comando
PEEK D DESPLAZAMIENTO_EN_MARCO_DE_PÁGINA
devuelve, invertida, la instrucción en octal en la dirección
virtual. Para comprobar esto, si el puntero de SICE apunta a esta dirección
virtual el comando
D EIP
devuelve el mismo valor en la ventana de datos de SICE. También
podemos verificar esto revisando el código en octal de la instrucción correspondiente.
Debemos ver lo mismo pero invertido. El comando
CODE ON
lo mostrará.
Continuemos con notepad.exe.
Ya tenemos los valores que corresponden a la dirección virtual
de la primera instrucción de notepad.exe
La dirección física de la página de directorio o directorio
primario lo obtenemos con el comando CPU de SICE: 008170000h. Traduzcámoslo
a binario:
0000 0000 1000 0001 0111 0000 0000 0000
Los primeros 20 bits de este número indican la dirección física
del comienzo del directorio primario del proceso activo. Los diez bits altos
de la dirección virtual nos dicen que se trata de la primera entrada en
este directorio
0000 0000 1000 0001 0111 - 0000 0000 01 - 00
^
^
bytes altos en CR3
- bytes altos
en dir virtual
obtenemos una nueva dirección física, la de la entrada 1 de
la tabla de pá-
ginas o directorio secundario: 00817004h.
Ejecutemos el comando PEEK D 817004.
PEEK D 00817004
SICE nos dá: 01EFE267
Obtenemos ahora la dirección física del directorio secundario.
En binario:
0000 0001 1110 1111 1110 0010 0110 0111
Los doce bits altos de este valor apuntan al inicio del directorio
secundario. Los diez bits medios indican una entrada dentro de este directorio.
1100 0001 1011 1011 1111 - 00 0000 0001 - 00
^
^
bytes altos en 01EFE267 - bytes
medios
en dir virtual
En hexadecimal: 01EFF004H
Esta entrada contiene la dirección del marco de página en
memoria física donde se encuentra instrucción indicada por la dirección
virtual.
Ahora obtengamos la dirección del marco de página:
PEEK D 01EFF004
Obtenemos un valor como 01DEC225h. Traduzcamos este valor
a binario:
0000 0001 1101 1110 1100 0010 0010 1001
Los 20 bits altos de este valor son la dirección física de
un marco de página, es decir, del comienzo de una página de memoria física.
Si agregamos a este valor el valor indicado por los doce bits bajos de la
dirección virtual obtenemos la dirección física donde se haya la instrucción
señalada por la dirección virtual:
0000 0001 1101 1110 1100 - 0000 0000 0000
^
^
bytes altos en 01DEC225h - bytes bajos
en dir virtual
PEEK D 01DEC0000
SICE devuelve 83EC8B55
El valor devuelto por este comando corresponde a la instrucción
en octal, pero invertido, a la que apunta la dirección virtual. Si esta
es la dirección señalada por el puntero de SICE el siguiente comando nos
devolverá el mismo valor regeresado antes:
D EIP
Vemos ahora que la ventana de datos de SICE apunta a 83EC8B55,
en la dirección EIP, el mismo valor devuelto con el comando 'PEEK D 01DEC0000'
Hagamos "code on" y veamos la correspondencia entre
el valor desplegado por el último PEEK y el código en octal desplegado por
SICE, que seguramente será el mismo pero invertido
55 push ebp
8BCE mov ebp, esp
83...
¿QUÉ VENTAJAS TIENE LA PAGINACIÓN?
====================================
Las ventajas de la paginación son:
Primero, las aplicaciones se aislan unas de otras.
Ningún proceso puede inadvertidamente (o maliciosamente) escribir encima
del espacio de código o datos de otro proceso, pues ni siquiera es capaz
de direccionar la memoria del otro proceso sin el valor CR3 adecuado, y
definir este valor es una tarea que sólo puede hacer el núcleo W32.
Segundo, este mecanismo de paginación resuelve uno
de los problemas más básicos en un entorno multitarea: la consolidación
de la memoria libre. En un esquema de direccionamiento más simple, a medida
que se ejecutan múltiples programas y se sale de ellos, la memoria se va
fragmentando. Si la memoria está demasiado fragmentada, los programas no
se pueden ejecutar porque no tienen suficiente memoria contigua, incluso
aunque la cantidad total de memoria libre sea la adecuada. Con la paginación,
no es necesario consolidar la memoria física libre porque las páginas no
tienen que ser contiguas. Cualquier cosa se gestiona manipulando las tablas
de página. La única pérdida viene realmente por el espacio perdido en las
propias tablas de página y la granularidad de las páginas de 4 KB.
Tercero, hay bits extra en las entradas de tabla de
página de 32 bits además de las direcciones de 20 bits. Un bit indica que
se ha accedido a una página particular (se denomina bit de «acceso»); otro
que la página ha sido escrita (el bit «sucio»). W32 puede usar estos bits
para determinar si una página de memoria se puede intercambiar (swapping)
a un archivo de disco para obtener más memoria física libre. Otro bit («presencia»)
indica si la página se ha intercambiado al disco y se tiene que recargar
en memoria.
Otro bit («lectura/escritura») indica si la página se puede
escribir. Este bit protege el código de los punteros errantes. Por ejemplo,
si se incluye la instrucción en código C siguiente en un programa Windows:
* (int *) WinMain 0;
se obtendrá un cuadro de mensaje diciendo: «Este programa
ha realizado una operación ilegal y se cerrará». Este bit no evita que un
programa compile el código fuente del programa y almacene las instrucciones
de lenguaje ensamblador en memoria para ser ejecutadas.
Las direcciones virtuales tienen un ancho de 32 bits. El código
y los datos (estáticos, de pila o localizados) de un programa tendrán direcciones
entre 0x00000000 y 0x7FFFFFFF. El propio W32 usa direcciones desde 0x80000000
a 0xFFFFFFFF y aquí es donde se encontrarán los puntos de entrada a las
librerías de enlace dinámico (DLL) de W32.
La cantidad total de memoria libre está determinada por la
cantidad de memoria física libre y el área libre de disco duro disponible
para intercambio de páginas. Como es normal con la gestión de memoria virtual,
W325 emplea un algoritmo LRU (Least-Recently Used: usado-menos-reciente
mente) para determinar qué páginas intercambiar a disco. Los bits de «acceso»
y «sucio» le ayudan en esta tarea. Las páginas de código no tienen que guardarse
en disco porque las páginas de código no son escribíbles, simplemente se
puede recargar desde el archivo .EXE o la librería de enlace dinámico (DLL).
A veces, se verá que se accede al disco cuando se mueve el
ratón desde el área cliente de un programa al área cliente de otro programa.
¿Por qué ocurre esto? Windows tiene que enviar mcnsajes de movimiento de
ratón a la segunda aplicación. Si el código del programa para procesar este
mensaje no está cargado actualmente en memoria, Windows tiene que recargarlo
desde el archivo de disco. Si tiene varios programas Windows grandes cargados
simultáneamente y no tiene mucha memoria, probablemente verá una actividad
inusual en el disco duro a medida que se mueve de programa a programa, pues
Windows está recargando páginas descargadas previamente.
A veces, los programas individuales se ralentizan (o se detienen
completamente) cuando Windows está realizando el intercambio (conmutación)
de páginas. Las páginas de código se pueden compartir entre aplicaciones.
Esto es particularmente útil para las librerías de enlace
dinámico. Varios programas ejecután-dose a la vez pueden usar las mismas
funciones Windows 95, sin que sea necesario que el mismo código se cargue
en memoria varias veces. Sólo es necesario una copia del código.
Excepto la granularidad de las páginas de 4 KB, la memoria
física no se puede fragmentar porque la desfragmentación implica únicamente
manipular las tablas de página. Sin embargo, la memoria virtual de una aplicación
se puede fragmentar si una aplicación localiza, relocaliza y libera varios
bloques de memoria. El limite de 2 MB para el código y los datos de una
aplicación normalmente es suficiente para evitar problemas. Es mucho más
fácil que un programa se quede antes sin memoria física que llegue a encontrar
un límite en la memoria virtual. Pero puede ocurrir, y si tiene un programa
donde este problema es concebible, puede que desee considerar la memoria
«movible».
¿EL PROGRAMADOR PUEDE GESTIONAR LA MEMORIA VIRTUAL?
========================================================
Sí, por supuesto. Para ello W32 proporciona varias funciones API:
Esta función se emplea para reservar y comprometer espacio
de memoria virtual:
LPVOID VirtualAlloc (LPVOID lpAddress,DWORD cbSize,
DWORD fdwAllocationType,
DWORD fdwProtec);
Para liberar el espacio de memoria comprometido:
BOOL VirtualFree (LPVOID lpAddress,SWORD cbSize,DWORD
fdwFreeType);
Bueno. Espero que este breve escrito sobre la gestión de memoria
virtual en W32 pueda entenderse y sea de utilidad.
Cualquier error u observación, por favor notifíquenme:
nuMIT_or@iname.com
|