Так как на современных компьютерах оперативная память RAM имеет размеры, по крайней мере, на порядок меньше 4 Гбайт, то система имитирует задекларированную память за счет дискового пространства. При этом на диске создается страничный файл (page file), который вместе с физической памятью RAM образует виртуальную память, доступную всем процессам. Другое название страничного файла — файл подкачки (swap file).
Процессор сам управляет отображением виртуальных адресов из машинных команд в эквивалентные физические адреса в ходе выполнения команды. Осуществляя это отображение, процессор оперирует страницами памяти размером 4 Кбайта. Этот же размер страниц используется Windows для управления виртуальной памятью.
Теперь посмотрим, что происходит, когда поток пытается получить доступ к блоку данных в адресном пространстве своего процесса. В этом случае возможны три ситуации:
Данные, к которым обращается поток, находятся в оперативной памяти. Тогда процессор проецирует виртуальный адрес данных на физический, и поток получает доступ к этим данным.
Данные отсутствуют в оперативной памяти, но размещены где-то в страничном файле. Попытка доступа к данным генерирует прерывание, называемое Page Fault (Ошибка страницы). Тогда система начинает искать свободную страницу в оперативной памяти. Если такой страницы нет, то система вынуждена освободить одну из занятых страниц. Если занятая страница не модифицировалась, она просто освобождается. В ином случае она сначала копируется из оперативной памяти в страничный файл. После этого система отыскивает в страничном файле запрошенный блок данных, загружает этот блок на свободную страницу оперативной памяти и, наконец, проецирует виртуальный адрес данных на соответствующий адрес в физической памяти.
Иногда из-за программной ошибки или сбоя в аппаратной части требуемая страница отсутствует и в оперативной памяти, и в страничном файле. Тогда система генерирует ошибку Invalid Page Fault, и работающее приложение закрывается.
Диспетчер виртуальной памяти (Virtual Memory Manager — VMM) является составной частью ядра операционной системы. Он отображает виртуальные адреса на физические, используя механизм подкачки страниц памяти (page swapping). Кроме того, он предоставляет прикладным программам различные интерфейсы (API) для работы с виртуальной памятью:
Virtual Memory API — набор функций, позволяющих приложению работать с виртуальным адресным пространством. Например, функции VirtualAlloc и VirtualFree позволяют процессу получать страницы из памяти или возвращать их системе.
Memory Mapped File API — набор функций, позволяющих работать с файлами, проецируемыми в память. Это новый механизм, предоставляемый Windows для работы с файлами и взаимодействия процессов.
Heap Memory API — набор функций, позволяющих работать с динамически распределяемыми областями памяти (кучами).
Проецирование файла данных в адресное пространство процесса предоставляет разработчику мощный механизм работы с файлами. Спроецировав файл на адресное пространство процесса, программа получает возможность работать с ним, как с массивом. Это очень удобно при манипуляциях с большими потоками данных. Для проецирования файла в память необходимо выполнить три операции:
1. Создать объект ядра «файл», идентифицирующий дисковый файл, который вы хотите использовать как проецируемый в память (функция CreateFile).
2. Создать объект ядра «проекция файла» при помощи функции CreateFileMapping. При этом используется дескриптор файла, возвращенный функцией CreateFile.
3. Указать отображение объекта «проекция файла» или его части на адресное пространство процесса (функция MapViewOfFile).
Закончив работу с проецируемым в память файлом, тоже следует выполнить три операции:
1. Отменить отображение на адресное пространство процесса объекта «проекция файла» (функция UnmapViewOfFile).
2. Закрыть объект ядра «проекция файла».
3. Закрыть объект ядра «файл».
Описанную технологию можно проиллюстрировать следующим фрагментом кода:
HANDLE hFile. hFileMapping:
PVOID pArray;
hFile = CreateFileC'File Name", ... );
hFileMapping = CreateFileMapping(hFile, ... ):
CloseHandle(hFile) :
pArray - MapViewOfFile(hFileMapping. ... );
CloseHandle(hFileMapping) ;
//
/ Работаем с файлом, как с массивом рАггау /
//
UnmapVi ewOfFi1е(pArray);
В этом примере «закрывающие» операции выполняются сразу после использования соответствующего дескриптора объекта. Это уменьшает вероятность утечки ресурсов.