¿Quién puede decirme el principio de la memoria virtual?
Introducción
La estructura de la memoria de Windows es la clave para una comprensión profunda de cómo funciona el sistema operativo Windows. A través de la comprensión de la estructura de la memoria, podemos comprender claramente cosas como la interfaz. -procesar datos** * Compartir, administrar eficazmente la memoria y otros problemas, para que el programa pueda ejecutarse de manera más eficiente durante el diseño del programa. El sistema operativo Windows puede administrar la memoria de muchas maneras diferentes, entre las cuales la administración de memoria virtual se puede utilizar para administrar grandes conjuntos de objetos y estructuras.
En los sistemas Windows, a cualquier proceso se le asigna su propio espacio de direcciones virtuales, que cubre un rango considerable. Para un proceso de 32 bits, su espacio de direcciones es 232=4, 294, 967, 296 Byte, que. permite que un puntero utilice cualquier valor dentro del rango de 4 GB, desde 0x00000000 hasta 0xFFFFFFFF. Aunque cada proceso de 32 bits puede usar 4 GB de espacio de direcciones, eso no significa que cada proceso en realidad tenga 4 GB de espacio de direcciones físicas. El espacio de direcciones es solo un espacio de direcciones virtuales, y este espacio de direcciones virtuales es solo un rango de direcciones de memoria. . La memoria física realmente disponible para un proceso es mucho más pequeña que su espacio de direcciones virtuales. El espacio de direcciones virtuales de un proceso es privado para cada proceso. El acceso al espacio de memoria por parte de los subprocesos que se ejecutan dentro del proceso está restringido al proceso que llama y no puede acceder a los espacios de memoria que pertenecen a otros procesos. De esta manera, se pueden utilizar punteros con la misma dirección en diferentes procesos para señalar el contenido que pertenece a los respectivos procesos de llamada sin causar confusión. Las tecnologías específicas de la memoria virtual se presentan a continuación.
Reserva y liberación de áreas en el espacio de direcciones
Cuando un proceso se crea inicialmente y se le asigna un espacio de direcciones, su espacio de direcciones virtuales aún no ha sido asignado y se encuentra en un estado inactivo. En este momento, la memoria en el espacio de direcciones no se puede utilizar. Cada área dentro de él primero debe asignarse a través de la función VirtualAlloc() y reservarse. El prototipo de la función VirtualAlloc() es:
LPVOID VirtualAlloc(
LPVOID lpAddress,
DWORD dwSize,
DWORD flAllocationType, p >
DWORD flProtect
);
El parámetro lpAddress contiene una dirección de memoria, que se utiliza para definir la primera dirección del área a asignar. Por lo general, este parámetro se puede establecer en NULL y el sistema buscará en el espacio de direcciones para determinar el espacio de direcciones no reservado que cumpla con las condiciones. En este momento, el sistema puede reservar un área a partir de cualquier posición en el espacio de direcciones y también puede especificar que se asigne memoria en la dirección más alta posible configurando el indicador MEM_TOP_DOWN en el parámetro flAllocationType. Si no desea que el sistema complete automáticamente la asignación del área de memoria y establezca la dirección de memoria para lpAddress (debe asegurarse de que siempre esté ubicada en la partición en modo de usuario del proceso; de lo contrario, la asignación fallará), entonces el sistema primero verificará si hay un espacio no reservado lo suficientemente grande en la dirección de memoria. Si hay un área libre lo suficientemente grande, el sistema reservará esta área y devolverá la dirección virtual de esta área reservada. Se devolverá NULL. Lo que hay que señalar aquí es que al especificar la dirección de memoria de lpAddress, debe asegurarse de que comience desde el límite de una granularidad de asignación.
En términos generales, la granularidad de asignación es diferente en diferentes plataformas de CPU, pero actualmente todas las CPU en entornos Windows como x86, Alpha de 32 bits, Alpha de 64 bits e IA-64 utilizan una granularidad de asignación de 64 KB. Si la dirección inicial del área reservada no sigue uno de los principios de comenzar desde el límite de la granularidad de asignación de 64 KB, el sistema ajustará automáticamente la dirección al múltiplo de 64 K más cercano.
Por ejemplo, si la lpAddress especificada es 0x00781022, entonces esta área reservada en realidad se asigna a partir de 0x00780000. El parámetro dwSize especifica el tamaño del área reservada. Sin embargo, el tamaño del área realmente reservada por el sistema debe ser un múltiplo entero del tamaño de la página de la CPU. Si el dwSize especificado no es un múltiplo entero del tamaño de la página de la CPU, el sistema lo ajustará automáticamente al múltiplo entero más cercano de la página. tamaño. Al igual que la granularidad de la asignación, el tamaño de la página también es diferente para las distintas plataformas de CPU. En la plataforma x86, el tamaño de la página es de 4 KB y en la plataforma Alpah de 32 bits, el tamaño de la página es de 8 KB. Cuando esté en uso, puede usar GetSystemInfo() para determinar el tamaño de página del host actual. Los parámetros flAllocationType y flProtect definen el tipo de asignación y los atributos de protección de acceso respectivamente. Dado que VirtualAlloc() se puede utilizar para reservar un área u ocupar memoria física, tiene sentido especificar si flAllocationType especifica el área actual que se reservará u ocupará memoria física. Los tipos de asignación de memoria que se pueden utilizar son:
Descripción del tipo de asignación
MEM_COMMIT asigna almacenamiento físico en la memoria o en el archivo de página del disco para un área de página específica
MEM_PHYSICAL Asigna memoria física (solo se usa para la memoria de extensión de la ventana de direcciones)
MEM_RESERVE Reserva el espacio de direcciones virtuales del proceso sin asignar ningún almacenamiento físico. Las páginas reservadas se pueden ocupar si se continúa llamando a VirtualAlloc()
MEM_RESET Indica que los datos especificados por los parámetros lpAddress y dwSize no son válidos en la memoria
MEM_TOP_DOWN Asigna memoria en la dirección más alta posible (Windows 98 ignora este indicador)
MEM_WRITE_WATCH debe especificarse junto con MEM_RESERVE para hacer que el sistema rastree aquellas páginas que se escriben en el área asignada (solo Windows 98)
Después del la asignación se completa con éxito, es decir, se reserva un área en el espacio de direcciones virtuales del proceso y se puede acceder a la memoria en esta área dentro del alcance de los permisos de protección. Cuando ya no sea necesario acceder a esta área del espacio de direcciones, esta área debe liberarse. Esto lo hace VirtualFree(). Su prototipo de función es:
BOOL VirtualFree(
LPVOID lpAddress,
DWORD dwSize,
DWORD dwFreeType
);
Entre ellos, el parámetro lpAddress es un puntero al área de la página que se va a publicar. Si el parámetro dwFreeType especifica MEM_RELEASE, entonces lpAddress debe ser la dirección base devuelta por VirtualAlloc() cuando el área de la página está reservada. El parámetro dwSize especifica el tamaño del área del espacio de direcciones que se liberará. Si el parámetro dwFreeType especifica el indicador MEM_RELEASE, dwSize se establece en 0 y el sistema calcula el tamaño del área que se liberará en una dirección de memoria específica. El parámetro dwFreeType es el tipo de operación de liberación realizada y sus valores posibles son MEM_RELEASE y MEM_DECOMMIT. El indicador MEM_RELEASE indica que el área de página reservada especificada se liberará y el indicador MEM_DECOMMIT desocupa el área de página ocupada especificada. Si VirtualFree() se ejecuta correctamente, se recuperará todo el rango de páginas asignadas. Cualquier acceso posterior a la memoria en estas áreas de páginas liberadas provocará una excepción de acceso a la memoria. El sistema puede asignar y utilizar el área de página liberada.
El siguiente código demuestra el proceso en el que el sistema reserva un área de 64 KB en la partición en modo de usuario del proceso y la libera:
// Reservado en el espacio de direcciones Un área
LPBYTE bBuffer = (LPBYTE)VirtualAlloc(NULL, 65536, MEM_RESERVE, PAGE_READWRITE);
……
// Liberar el área reservada
VirtualFree(bBuffer, 0, MEM_RELEASE);
Envío y reciclaje de memoria física
Después de reservar un área en el espacio de direcciones, no se puede procesar directamente usando la memoria. Solo se puede acceder a la dirección en el área después de que la memoria física se envíe al área. Durante el proceso de confirmación, la memoria física se confirma en límites de página y bloques del tamaño de una página. Para confirmar la memoria física para un área de espacio de direcciones reservada, debe llamar a la función VirtualAlloc() nuevamente. La diferencia es que debe especificar el parámetro flAllocationType como el indicador MEM_COMMIT durante el proceso de confirmación de la memoria física. atributo y el área reservada Las propiedades de protección utilizadas son consistentes. Al realizar la confirmación, el almacenamiento físico se puede confirmar en toda el área reservada o parcialmente. El parámetro lpAddress y el parámetro dwSize de la función VirtualAlloc() indican dónde y cuánto almacenamiento físico confirmar.
De manera similar a la liberación del área reservada, cuando la memoria física comprometida en el área reservada ya no es necesaria, la memoria física comprometida debe liberarse a tiempo. El proceso de reciclaje también se completa a través de la función VirtualFree() como la liberación del área reservada. Especifique el indicador MEM_DECOMMIT para el parámetro dwFreeType de VirtualFree() al llamar y pase la dirección de memoria utilizada para identificar la primera página que se liberará y la cantidad de bytes que se liberarán en los parámetros lpAddress y dwSize. Este proceso de reciclaje también se realiza en unidades de páginas, y todas las páginas involucradas en el rango establecido se reciclarán. El siguiente código demuestra el proceso de confirmar un área previamente reservada y reciclarla después de su uso:
// Reservar un área en el espacio de direcciones
LPBYTE bBuffer = ( LPBYTE)VirtualAlloc(NULL , 65536, MEM_RESERVE, PAGE_READWRITE);
//Confirmar almacenamiento físico
VirtualAlloc(bBuffer, 65536, MEM_COMMIT, PAGE_READWRITE);
……
//Reciclar la memoria física comprometida
VirtualFree(bBuffer, 65536, MEM_DECOMMIT);
//Liberar el área reservada
VirtualFree(bBuffer , 0, MEM_RELEASE);
Dado que el área reservada no comprometida en realidad no se puede utilizar, se permite completar el espacio de direcciones mediante una llamada a VirtualAlloc() durante el proceso de programación y se compromete al almacenamiento físico para la reserva. regiones. En consecuencia, el proceso de reciclaje y liberación también se puede implementar mediante una llamada a VirtualFree().
El código anterior se puede reescribir de esta manera:
// Reserve un área en el espacio de direcciones y confirme la memoria física
LPBYTE bBuffer = (LPBYTE)VirtualAlloc(NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
......
// Libera el área reservada y recupera la memoria física comprometida
VirtualFree(bBuffer, 0, MEM_RELEASE | MEM_DECOMMIT);
El uso de archivos de página
El almacenamiento físico se ha mencionado muchas veces antes. El almacenamiento físico mencionado aquí no se limita a la memoria de la computadora, sino que también incluye la creación. en el espacio en disco. El tamaño del espacio de almacenamiento del archivo de paginación es la suma de la capacidad de almacenamiento de la memoria de la computadora y el archivo de paginación. Dado que el espacio de almacenamiento en disco suele ser mucho mayor que el espacio de almacenamiento en memoria, el uso de archivos de página equivale a un aumento transparente en la capacidad de memoria que la aplicación puede usar. Cuando está en uso, el sistema operativo y la CPU son responsables de mantener y coordinar el archivo de página. Solo cuando la aplicación lo necesita, los datos del archivo de página se cargan temporalmente en la memoria para el acceso de la aplicación y luego se vuelven a intercambiar al archivo de página desde la memoria después de su uso.
Cuando un subproceso en un proceso accede a una dirección de memoria ubicada en un área reservada de la memoria física comprometida, si los datos señalados por esta dirección existen actualmente en la memoria, la CPU asignará directamente la dirección virtual del proceso a una dirección física y complete el acceso a los datos si los datos existen en el archivo de la página, intente cargar los datos del archivo de la página en la memoria. Al realizar este proceso, primero verifique si hay una página libre en la memoria que pueda usarse. Si la hay, los datos se pueden cargar directamente en la página libre en la memoria. De lo contrario, es necesario encontrar una página liberable. La página aún no está en uso y carga datos en esta página. Si los datos de la página publicada siguen siendo datos válidos (es decir, se utilizarán en el futuro), la página primero debe escribirse desde la memoria en el archivo de la página. Una vez que los datos se cargan en la memoria, aún es necesario lograr el acceso a los datos después de que la CPU asigne la dirección virtual a una dirección física. A diferencia del acceso a datos en la memoria física, cuando se ejecuta un programa ejecutable, el código y los datos del programa no se copian del archivo del disco al archivo de página, sino que, una vez determinado el tamaño del código del programa y sus datos, el sistema lo envía directamente. utiliza la imagen del programa ejecutable como el área de espacio de direcciones reservada del programa. Este método de procesamiento acorta en gran medida el tiempo de inicio del programa y reduce el tamaño del archivo de página.
Gestión de la memoria
Utilizando la tecnología de memoria virtual podrás gestionar la memoria. Se puede obtener información dinámica sobre el estado actual de la memoria a través de la función GlobalMemoryStatus(). El prototipo de función de GlobalMemoryStatus() es:
VOID GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer);
El parámetro lpBuffer es un puntero a la estructura de estado de la memoria MEMORYSTATUS, y el objeto de estructura debe configurarse de antemano Se inicializan los miembros de datos.
La estructura MEMORYSTATUS se define de la siguiente manera:
typedef struct _MEMORYSTATUS {
DWORD dwLength; // Tamaño de la estructura MEMORYSTATUS
DWORD dwMemoryLoad;
DWORD dwTotalPhys; // El número total de bytes de memoria física
DWORD dwAvailPhys; // El número de bytes de memoria física libre
DWORD dwTotalPageFile; // El número máximo de bytes contenidos en el archivo de página
DWORD dwAvailPageFile; // El número de bytes disponibles en el archivo de página
DWORD dwTotalVirtual // Tamaño de partición en modo de usuario
DWORD dwAvailVirtual; // El tamaño de la memoria libre en la partición del modo de usuario
} MEMORYSTATUS, *LPMEMORYSTATUS
El siguiente código actualiza la configuración actual del sistema cada; 5 segundos configurando un temporizador Uso de memoria:
//Establecer temporizador
SetTimer(0, 5000, NULL);
……
void CSample22Dlg::OnTimer(UINT nIDEvent)
{
// Obtener el estado de uso de memoria actual
MEMORYSTATUS mst; > GlobalMemoryStatus(amp ; mst);
// Porcentaje de memoria utilizada
m_dwMemoryLoad = mst.dwMemoryLoad
// Número total de bytes de memoria física;
m_dwAvailPhys = mst.dwAvailPhys / 1024;
// El número de bytes de memoria física libre
m_dwAvailPageFile = mst.dwAvailPageFile / 1024; >
// El número máximo de bytes contenidos en el archivo de página
m_dwAvailVirtual = mst.dwAvailVirtual / 1024
// El número de bytes disponibles en el archivo de página
m_dwTotalPageFile = mst.dwTotalPageFile / 1024;
// Tamaño de partición en modo usuario
m_dwTotalPhys = mst.dwTotalPhys / 1024; // Memoria libre en la partición en modo de usuario Tamaño
m_dwTotalVirtual = mst.dwTotalVirtual / 1024
// Actualizar pantalla
UpdateData(FALSE); p>
CDialog::OnTimer (nIDEvent);
}
Además de obtener la información del estado de uso de la memoria actual, la administración de la memoria a menudo también necesita obtener información del estado sobre la espacio de direcciones virtuales del proceso.
Puede ser consultado mediante la función VirtualQuery() y su prototipo se declara de la siguiente manera:
DWORD VirtualQuery(
LPCVOID lpAddress, // Dirección de memoria
PMEMORY_BASIC_INFORMATION lpBuffer, // Puntero a la estructura de información de la memoria
DWORD dwLength //El tamaño de la memoria
El parámetro lpAddress es la memoria virtual. dirección que se va a consultar y este valor se ajustará al límite de página más cercano. El tamaño de página de la computadora actual se puede obtener a través de la función GetSystemInfo (), que requiere un puntero a la estructura SYSTEM_INFO como parámetro, y la información del sistema obtenida se completará en el objeto de estructura de datos. El siguiente código obtiene la información actual del sistema llamando a GetSystemInfo():
// Obtener la información actual del sistema
GetSystemInfo(amp; m_sin);
/ / Enmascaramiento de bits, que indica qué CPU está activa
m_dwActiveProcessorMask = m_sin.dwActiveProcessorMask;
// Granularidad de asignación del área de espacio de direcciones reservada
m_dwAllocationGranularity = m_sin.dwAllocationGranularity ;
//La dirección de memoria mínima del espacio de direcciones disponible del proceso
m_dwMaxApplicationAddress = (DWORD)m_sin.lpMaximumApplicationAddress;
// La dirección de memoria máxima de el espacio de direcciones disponible del proceso
m_dwMinApplicationAddress = (DWORD)m_sin.lpMinimumApplicationAddress;
//El número de CPU en la computadora
m_dwNumberOfProcessors = m_sin.
//Tamaño de página
m_dwPageSize = m_sin.dwPageSize;
//Tipo de procesador
m_dwProcessorType = m_sin.dwProcessorType ; p>
//Subdividir aún más el nivel del procesador
m_wProcessorLevel = m_sin.wProcessorLevel;
//La estructura del procesador del sistema
m_wProcessorArchitecture = m_sin .wProcessorArchitecture;
//Actualizar visualización
UpdateData(FALSE);
El segundo parámetro lpBuffer de VirtualQuery() es un puntero al puntero de estructura MEMORY_BASIC_INFORMATION. Si VirtualQuery() se ejecuta correctamente, la información del estado del espacio de direcciones virtuales consultado se guardará en este objeto de estructura.
La estructura MEMORY_BASIC_INFORMATION se define como:
typedef struct _MEMORY_BASIC_INFORMATION {
PVOID BaseAddress; // La dirección base del área reservada
PVOID AllocationBase; dirección base asignada
DWORD AllocationProtect // El atributo de protección establecido al retener por primera vez
DWORD RegionSize // Tamaño de región
DWORD State /; / Estado (enviar, Reservado o libre)
DWORD Protect; // Atributo de protección de acceso actual
Tipo DWORD; // Tipo de página
} MEMORY_BASIC_INFORMATION; /p>
La información de estado relevante se obtiene consultando el área del espacio de direcciones virtuales especificada por los parámetros lpAddress y dwLength a través de la función VirtualQuery():
//Actualizar visualización
UpdateData(TRUE) ;
//Estructura de estado del espacio de direcciones virtuales
MEMORY_BASIC_INFORMATION mbi;
//Consulta la información de estado del espacio de direcciones virtuales especificado p>
VirtualQuery ((LPCVOID)m_dwAddress, & mbi, 1024);
// Dirección base del área reservada
m_dwBaseAddress = (DWORD)mbi.BaseAddress; p>
// Dirección base asignada
m_dwAllocateBase = (DWORD)mbi.AllocationBase;
// Atributo de protección establecido cuando se reservó inicialmente
m_dwAllocateProtect = mbi.AllocationProtect;
//Tamaño de región
m_dwRegionSize = mbi.RegionSize;
//Estado (comprometido, reservado o libre)
m_dwState = mbi.State;
//Atributo de protección de acceso actual
m_dwProtect = mbi.Protect;
//Tipo de página
m_dwType = mbi.Type;
//Actualizar pantalla
UpdateData(FALSE);
Resumen
Este artículo se centra principalmente. sobre la gestión de la memoria Se presentan los principios básicos, el uso y la gestión de la memoria de la tecnología de memoria virtual. A través de este artículo, podrá dominar el uso general de la memoria virtual. Las tecnologías de administración de memoria relacionadas también incluyen el mapeo de archivos de memoria y la administración del montón. Todas estas tecnologías de administración de memoria son tecnologías avanzadas en la programación de Windows y su uso apropiado en las aplicaciones ayudará a mejorar el rendimiento del programa. El programa descrito en este artículo está compilado por Microsoft Visual C 6.0 en Windows 2000 Professional.