Отображение файлов в память

Материал из DRKB

Отображение файлов в память

Для работы с файлом динамической подкачки страниц виртуальной памяти в Windows 32 используется механизм отображения файлов в адресное пространство программы. Соответствующие функции API доступны любой программе и могут применяться к любому файлу (кстати, таким способом загружается в адресное пространство процесса исполняемый файл). В результате отображения программа может работать с файловыми данными как с данными, размещенными в динамической памяти. Такая возможность не только в большинстве случаев повышает скорость работы с данными, но и предоставляет программисту уникальные средства обработки сразу всех записей файла. Например, он может единственным оператором проверить входит ли заданный образец поиска в любую строку текстового файла. Отображение файла осуществляется в три приема.

Создание/открытие файла[править | править код]

Вначале файл создается обращением к функции

function FileCreate(FileName: String): Integer;

или открывается с помощью

function FileOpen(const FileName: String; Mode: LongWord): Integer;

В обеих функциях FileName - имя файла, возможно, с маршрутом доступа. Параметр Mode определяет режим доступа к файлу и может принимать одно из следующих значений:

  • fmOpenRead - только чтение;
  • fmOpenWrite - только Запись;
  • fmOpenReadWrite - чтение и запись.

C помощью операции or эти константы можно комбинировать с одной из следующих регулирующих совместный доступ к файлу нескольких Программ:

  • fmShareExclusive - совместный доступ Запрещен;
  • fmShareDenyWrite - Другим Программам запрещается запись;
  • fmShareDenyRead - другим программам запрещается чтение;
  • fmSchareDenyNone - совместный доступ неограничен.

Обе функции возвращают дескриптор созданного (открытого) файла или 0, если операция оказалась неуспешной.


Создание объекта отображения[править | править код]

На втором этапе создается объект отображения в память. Для этого используется такая функция:

function CreateFileMapping(hFile: THandle; IpFileMappingAttributes: PSecurityAttributes;
  flProtect, dwMaximumSizeHigh, dwMaximumSizeLow: DWord; IpName: PChar): THandle;
  • hFile - дескриптор файла;
  • ipFileMappingAttributes - указатель на структуру, в которой определяется, может ли создаваемый объект порождать дочерние объекты (обычно не может - nil);
  • flProtect - определяет тип защиты, применяемый к окну отображения файла (см.ниже);
  • dwMaximumSizeHigh, dwMaximumSizeLow - соответственно Старшие и младшие 32 разряда размера файла; если вы будете отображать файлы длинной до 4 ГбаЙТ, поместите В dwMaximumSizeHigh 0, а В dwMaximumSizeLow - длину файла; если оба параметра равны 0, размер окна отображения равен размеру файла;
  • ipName - имя объекта отображения или 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(IpBaseAddress: Pointer): BOOL;

единственный параметр обращения к которой должен содержать адрес первого отображенного байта, т. е. адрес, возвращаемый функцией MapViewOfFile. Закрытие объекта отображения и самого файла осуществляется обращением к функции

function CloseHandle(hObject: THandle)


Пример использования[править | править код]

Программа создает дисковый файл, состоящий из 100000 случайных вещественных чисел (длину файла можно выбрать другой, если изменить значение редактора длина массива). Файл с именем test.dat создается путем отображения файла в память (кнопка память) и традиционным способом (кнопка Файл). В обоих случаях показывается время счета (процессор 400 МГц, память 64 Мбайт). Чем больше частота и память, тем больше будет разница во времени.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,   StdCtrls, ComCtrls, Spin;

type
  TFormI = class (TForm)
    btMem: TButton;
    btFile: TButton;
    se: TSpinEdit;
    Labell: TLabel;
    pb: TProgressBar;
    Label2: TLabel;
    IbMem: TLabel;
    IbFile: 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, О, О, 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));
        IbMem.Caption := IntToStr(k);
        pb.Position := k;
        Application.ProcessMessages;
      end;
      // Освобождаем окно просмотра:
      UnmapViewOfFile(AdrBase)
    finally
      // Освобождаем отображение
      CloseHandle(HMap);
    end;
  finally
   // Закрываем файл
   CloseHandle(HFile);
  end;
  // Сообщаем время счета
  pb.Hide;
  IbMem.Caption := TimeToStr(Time - BegTime);
end;

// Создание файла обычным методом var
procedure TFormI.btFileClick(Sender: TObject) ;
  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); // могут быть только переменные
    IbFile.Caption := IntToStr(k);
    pb.Position := k;
    Application.ProcessMessages;
  end;
  CloseFile(F) ;
  pb.Hide;
  IbFile.Caption := TimeToStr(Time - BegTime)
end;

end.


ID: 03123
ID: 03134