Для работы с файлом динамической подкачки страниц виртуальной памяти в Windows 32 используется механизм отображения файлов в адресное пространство приложения. Соответствующие функции API доступны любому приложению и могут применяться к любому файлу (кстати, таким способом загружаются в адресное пространство процесса исполняемые файлы и DLL). В результате отображения приложение может работать с файловыми данными как с размещенными в динамической памяти. В большинстве случаев такая возможность не только повышает скорость работы с данными, но и предоставляет программисту уникальные средства обработки сразу всех записей файла. Например, он может с помощью единственного оператора проверить, входит ли заданный образец поиска в какую-либо строку текстового файла.
Отображение файла осуществляется в три приема. Вначале файл создается обращением к функции:
function FileCreate (FileName: String): Integer;
или открывается с помощью:
function FileOpen (const FileName: String; Mode: LongWord): Integer;
В обеих функциях FileName — имя файла, возможно, с маршрутом доступа. Параметр Mode определяет режим доступа к файлу и может принимать одно из следующих значений: fmOpenRead — только чтение; fmOpenWrite — только запись; fmOpenReadWrite — чтение и запись. С помощью операции or эти константы можно комбинировать с одной из следующих нескольких функций, регулирующих совместный доступ к файлу: fmShareExclusive — совместный доступ запрещен; fmShareDenyWrite — другим приложениям запрещается запись; fmShareDenyRead — другим приложениям запрещается чтение; fmSchareDenyNone — совместный доступ неограничен. Обе функции возвращают дескриптор созданного (открытого) файла или 0, если операция оказалась неудачной.
На втором этапе создается объект отображения в память. Для этого используется функция:
function CreateFileMapping (hFile: THandle; lpFileMappingAttributes: PSecurityAttributes; flProtect, dwMaximumSizeHigh, dwMaximumSizeLow: DWord; lpName: PChar): THandle;
Здесь hFile — дескриптор файла; lpFileMappingAttributes — указатель на структуру, в которой определяется, может ли создаваемый объект порождать дочерние объекты (обычно не может — NIL); flProtect — определяет тип защиты, применяемый к окну отображения файла (см. об этом ниже); dwMaximumSizeHigh, dwMaximumSizeLow — соответственно старшие и младшие 32 разряда числа, содержащего размер файла (если вы будете отображать файлы длиной до 4 Гбайт, поместите в dwMaximumSizeHigh 0, если в dwMaximumSizeLow — длину файла; а если оба параметра равны 0, то размер окна отображения равен размеру файла); lpName — имя объекта отображения или NIL.
Параметр flProtect задает тип защиты, применяемый к окну просмотра файла, и может иметь одно из следующих значений: PAGE_READONLY — файл можно только читать (файл должен быть создан или открыт в режиме fmOpenRead); PAGE_READWRITE — файл можно читать и записывать в него новые данные (файл открывается в режиме fmOpenReadWrite); PAGE_WRITECOPY — файл открыт для записи и чтения, однако обновленные данные сохраняются в отдельной защищенной области памяти (отображенные файлы могут разделяться приложениями, в этом режиме каждое приложение сохраняет изменения в отдельной области памяти или участке файла подкачки); файл открывается в режиме fmOpenReadWrite или fmOpenWrite; (этот тип защиты нельзя использовать в Windows 95/98). С помощью операции or к параметру flProtect можно присоединить такие атрибуты: SEC_COMMIT — выделяет для отображения физическую память или участок файла подкачки; SEC_IMAGE — информация об атрибутах отображения берется из образа файла; SEC_NOCASHE — отображаемые данные не кэшируются и записываются непосредственно на диск; SEC_RESERVE — резервируются страницы раздела без выделения физической памяти.
Функция возвращает дескриптор объекта отображения или 0, если обращение было неудачным.
Наконец, на третьем этапе создается окно просмотра, то есть собственно отображение данных в адресное пространство программы.
function MapViewOfFile(hFileMappingObject:
THandle;dwDesiresAccess: DWord; dwFileOffsetHigh, dwFileIffsetLow,
dwNumberOfBytesToMap: DWord): Pointer;
Здесь hFileMappingObject — дескриптор объекта отображения; dwDesiresAccess — определяет способ доступа к данным и может иметь одно из следующих значений: FILE_MAP_WRITE — разрешает чтение и запись (при этом в функции CreateFileMapping должен использоваться атрибут PAGE_READWRITE); FILE_MAP_READ — разрешает только чтение (в функции CreateFileMapping должен использоваться атрибут PAGE_READONLY или PAGE_READWRITE); FILE_MAP_ALL_ACCESS — то же, что и FILE_MAP_WRITE; FILE_MAP_COPY — данные доступны для записи и чтения, однако обновленные данные сохраняются в отдельной защищенной области памяти (в функции CreateFileMapping должен использоваться атрибут PAGE_WRITECOPY); dwFileOffsetHigh, dwFileIffsetLow — определяют соответственно старшие и младшие разряды смещения от начала файла, начиная с которого осуществляется отображение; dwNumberOfBytesToMap — определяет длину окна отображения (0 — длина равна длине файла). Функция возвращает указатель на первый байт отображенных данных или NIL, если обращение к функции оказалось безуспешным.
После использования отображенных данных ресурсы окна отображения нужно освободить функцией:
function UnMapViewOfFile(lpBaseAddress: Pointer): BOOL;
единственный параметр обращения к которой должен содержать адрес первого отображенного байта, то есть адрес, возвращаемый функцией MapViewOfFile. Закрытие объекта отображения и самого файла осуществляется обращением к функции:
function CloseHandle(hObject: THandle)
В листинге 3 приводится текст модуля (FileInMemory.dpr), который создает окно.
Листинг 3
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, Spin;
type
TForm1 = class(TForm)
btMem: TButton;
btFile: TButton;
se: TSpinEdit;
Label1: TLabel;
pb: TProgressBar;
Label2: TLabel;
lbMem: TLabel;
lbFile: TLabel;
procedure btMemClick(Sender: TObject);
procedure btFileClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.btMemClick(Sender: TObject);
// Создание файла методом его отображения
type
PReal = ^Real;
var
HFile, HMap: THandle;
AdrBase, AdrReal: PReal;
k: Integer;
FSize: Cardinal;
BegTime: TDateTime;
begin
BegTime := Time; // Засекаем время пуска
// Готовим ProgressBar:
pb.Max := se.Value;
pb.Position := 0;
pb.Show;
FSize := se.Value * SizeOf(Real); // Длина файла
HFile := FileCreate('test.dat'); // Создаем файл
if HFile = 0 then // Ошибка: возбуждаем исключение
raise Exception.Create('Ошибка создания файла');
try
// Отображаем файл в память
HMap := CreateFileMapping(
HFile, NIL, PAGE_READWRITE, 0, FSize, NIL);
if HMap = 0 then // Ошибка: возбуждаем исключение
raise Exception.Create('Ошибка отображения файла');
try
// Создаем окно просмотра:
AdrBase := MapViewOfFile(HMap, FILE_MAP_WRITE, 0, 0, FSize);
if AdrBase = NIL then // Ошибка: возбуждаем исключение
raise Exception.Create('Невозможно просмотреть файл');
// Сохраняем начальный адрес для правильной ликвидации
// окна просмотра:
AdrReal := AdrBase;
for k := 1 to se.Value do
begin
AdrReal^ := Random; // Помещаем в файл новое число
// Перед наращиванием текущего адреса необходимо
// привести его к типу Integer или Cardinal:
AdrReal := Pointer(Integer(AdrReal) + SizeOf(Real));
lbMem.Caption := IntToStr(k);
pb.Position := k;
Application.ProcessMessages;
end;
// Освобождаем окно просмотра:
UnmapViewOfFile(AdrBase)
finally
// Освобождаем отображение
CloseHandle(HMap)
end
finally
// Закрываем файл
CloseHandle(HFile)
end;
// Сообщаем время счета
pb.Hide;
lbMem.Caption := TimeToStr(Time-BegTime)
end;
procedure TForm1.btFileClick(Sender: TObject);
// Создание файла обычным методом
var
F: File of Real;
k: Integer;
BegTime: TDateTime;
R: Real; // Буферная переменная для обращения к Write
begin
BegTime := Time; // Засекаем начальное время счета
// Готовим ProgressBar:
pb.Max := se.Value;
pb.Position := 0;
pb.Show;
// Создаем файл:
AssignFile(F, 'test.dat');
Rewrite(F);
for k := 1 to se.Value do
begin
R := Random; // Параметрами обращения к Write
Write(F, R); // могут быть только переменные
lbFile.Caption := IntToStr(k);
pb.Position := k;
Application.ProcessMessages;
end;
CloseFile(F);
pb.Hide;
lbFile.Caption := TimeToStr(Time-BegTime)
end;
end.
Проект создает дисковый файл, состоящий из 100 тыс. случайных вещественных чисел (можно выбрать другую длину файла, если изменить значение редактора Длина массива). Файл с именем test.dat создается путем отображения файла в память (кнопка Память) и традиционным способом (кнопка Файл). В обоих случаях показывается время счета. Чем больше частота процессора и объем свободной оперативной памяти, тем больше будет разница во времени (листинг 3).