Ограничения метода отображения файлов

Как уже отмечалось ранее, отображение файлов является весьма мощным и полезным средством. Существующая в Windows диспропорция между 64-битовой файловой системой и 32-битовой адресацией снижает ценность обеспечиваемых этим средством преимуществ; Win64 свободен от этих ограничений.

Основная проблема заключается в том, что при больших размерах файлов (в данном случае, свыше 2—3 Гбайт) отображение всего файла на пространство виртуальной памяти становится невозможным. Более того, не будут доступны даже все 3 Гбайт, поскольку часть виртуального адресного пространства распределяется для других целей, а суммарный объем доступных смежных блоков будет гораздо меньше теоретического максимума. Win64 в значительной степени снимает это ограничение.

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

Двумя другими существенными недостатками метода отображения файлов являются следующие:

• Размер объекта отображения не может увеличиваться. Создавая объект отображения, вы должны заранее определить, какой максимальный размер вам может понадобиться, но во многих случаях сделать это трудно или вообще невозможно.

• Невозможно распределить память в пределах области, занимаемой представлением объекта отображения, не создав для этого собственные функции управления памятью. Было бы очень удобно, если бы существовал способ задавать объект отображения файла и указатель, возвращаемый функцией MapViewOfFile, с последующим получением дескриптора кучи.

Резюме: отображение файлов

Ниже приведена стандартная последовательность действий, которые необходимо выполнять, если используется отображение файлов:

1. Откройте файл. Убедитесь в том, что он имеет права доступа GENERIC_READ.

2. В случае создания нового файла укажите его размер, используя для этого либо функцию CreateFileMapping (шаг 3), либо функцию SetFilePointer с последующим вызовом функции SetEndOfFile.

3. Отобразите файл при помощи функций CreateFileMapping или OpenFileMapping.

4. Создайте одно или несколько представлений объекта отображения файла с помощью функции MapViewOfFile.

5. Осуществляйте доступ к файлу через обращения к памяти. Для перехода к другим участкам отображаемого файла используйте функции UnmapViewOfFile и MapViewOfFile.

6. Завершив работу, вызовите последовательно функции UnmapViewOfFile, CloseHandle для закрытия дескриптора объекта отображения и CloseHandle для закрытия дескриптора файла.

Пример: последовательная обработка файлов с использованием метода отображения

Программа atou (программа 2.4) иллюстрирует последовательную обработку файлов на примере преобразования ASCII-файлов к кодировке Unicode, приводящего к удвоению размера файла. Этот случай является идеальным для применения отображения файлов, поскольку наиболее естественным способом указанного преобразования является посимвольная обработка данных без обращения к операциям файлового ввода/вывода. Программа 5.3 сначала просто отображает входной и выходной файлы в память, предварительно вычисляя размер выходного файла путем удвоения размера входного файла, а затем осуществляет требуемое посимвольное преобразование.

Этот пример отчетливо демонстрирует, как сложность процесса отображения файлов, выполнение которого необходимо для инициализации программы, компенсируется простотой результирующей обработки. Принимая во внимание, насколько просто выполняются обычные операции файлового ввода/вывода, применение более сложного метода могло бы показаться излишним, однако это с лихвой окупается выигрышем в производительности. В приложении В показано, что версия, использующая отображение файлов, в файловых системах NTFS работает значительно быстрее по сравнению с версиями, использующими обычные способы доступа к файлам, так что некоторое усложнение программы себя полностью оправдывает. Дополнительные результаты анализа производительности приведены на Web-сайте книги, поэтому ниже мы ограничимся лишь краткими выводами.

• Повышение производительности программ за счет использования отображения файлов наблюдается только в случае Windows NT и файловых систем NTFS.

• По сравнению с наилучшими из методик последовательной обработки файлов обеспечивается, по крайней мере, трехкратное повышение производительности .

• При работе с файлами большого размера преимущества в отношении производительности теряются. В нашем примере обычный последовательный просмотр файлов оказывается более предпочтительным, так как размер входного файла составляет около одной трети объема физической памяти. Снижение производительности метода отображения файлов в данном случае объясняется тем, что для входного файла требуется одна треть памяти, а для выходного файла, размер которого в два раза больше, — оставшиеся две трети, что заставляет нас сбрасывать отдельные части выходного файла на диск. Таким образом, в системе с объемом оперативной памяти 192 Мбайт ухудшение производительности метода отображения файлов будет наступать после достижения входными файлами размера 60 Мбайт. В большинстве случаев приходится иметь дело с файлами меньшего размера, в результате чего применение метода отображения файлов становится целесообразным.

В программе 5.3 представлена лишь функция Asc2UnMM. Основная программа совпадает с той, которая приведена в программе 2.4.

Программа 5.3. Asc2UnMM: преобразование файла с использованием метода отображения файлов

/* Глава 5. Asc2UnMM.c: Реализация, использующая отображение файлов. */

#include "EvryThng.h"

 

BOOL Asc2Un(LPCTSTR fin, LPCTSTR fOut, BOOL bFaillfExists) {

HANDLE hIn, hOut, hInMap, hOutMap;

LPSTR pIn, pInFile;

LPWSTR pOut, pOutFile;

DWORD FsLow, dwOut;

/* Открыть и отобразить входной и выходной файлы. */

hIn = CreateFile(fIn, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

hInMap = CreateFileMapping(hIn, NULL, PAGE_READONLY, 0, 0, NULL);

pInFile = MapViewOfFile(hInMap, FILE_MAP_READ, 0, 0, 0);

dwOut = bFailIfExists ? CREATE NEW : CREATE ALWAYS;

hOut = CreateFile(fOut, GENERIC_READ | GENERIC_WRITE, 0, NULL, dwOut, FILE_ATTRIBUTE_NORMAL, NULL);

FsLow = GetFileSize (hIn, NULL); /* Установить размер отображения. */

hOutMap = CreateFileMapping(hOut, NULL, PAGE_READWRITE, 0, 2* FsLow, NULL);

pOutFile = MapViewOfFile(hOutMap, FILE_MAP_WRITE, 0, 0, (SIZE_T)(2 * FsLow));

/* Преобразовать данные отображенного файла из ASCII в Unicode. */

pIn = pInFile;

pOut = pOutFile;

while (pIn < pInFile + FsLow) {

*pOut = (WCHAR) *pIn;

pIn++;

pOut++;

}

UnmapViewOfFile(pOutFile);

UnmapViewOfFile(pInFile);

CloseHandle(hOutMap);

CloseHandle(hInMap);

CloseHandle(hIn);

CloseHandle(hOut);

return TRUE;

}

Пример: сортировка отображенных файлов

Дополнительным преимуществом метода отображения файлов является то, что он допускает применение обычных алгоритмов обработки файлов в памяти компьютера. Так, сортировку данных в памяти осуществить гораздо легче, чем сортировку записей в файле.

Программа 5.4 предназначена для сортировки файлов с записями фиксированной длины. Данная программа, sortFL, аналогична программе 5.1 в том отношении, что предполагает наличие 8-байтового ключа сортировки в начале записи, но ограничивается записями фиксированной длины. В программе 5.5 этот недостаток будет устранен за счет некоторого усложнения программы.

Сортировку выполняет описанная в файле <stdlib.h> функция qsort, входящая в состав библиотеки С. Заметьте, что эта функции требует от программиста предоставления функции, осуществляющей сравнение записей, в качестве которой нами будет использована функция KeyCompare из программы 5.2.

Структура программы достаточно проста. Сначала на основе временной копии входного файла создается объект отображения файла, затем создается единое представление объекта отображения файла в памяти, и, наконец, вызывается функция qsort. При этом какие-либо операции файлового ввода/вывода отсутствуют. Отсортированный файл направляется далее на стандартный вывод, причем в конце отображения файла добавляется нулевой символ.

Программа 5.4. sortFL: сортировка файла с использованием его отображения в памяти

/* Глава 5. sortFL. Сортировка файлов. Записи имеют фиксированную длину.*/

/* Использование: sortFL файл */

#include "EvryThng.h"

 

typedef struct _RECORD {

TCHAR Key[KEY_SIZE];

TCHAR Data[DATALEN];

} RECORD;

 

#define RECSIZE sizeof(RECORD)

 

int _tmain(int argc, LPTSTR argv[]) {

HANDLE hFile = INVALID_HANDLE_VALUE, hMap = NULL;

LPVOID pFile = NULL;

DWORD FsLow, Result = 2;

TCHAR TempFile[MAX_PATH];

LPTSTR pTFile;

/* Создать имя временного файла, предназначенного для хранения копии сортируемого файла, которая и подвергается сортировке. */

/* Можно действовать и по-другому, оставив файл в качестве постоянно хранимой сортируемой версии. */

_stprintf(TempFile, _T("%s%s"), argv[1], _Т(".tmp"));

CopyFile(argv[1], TempFile, TRUE);

Result = 1; /* Временный файл является вновь созданным и должен быть удален. */

/* Отобразить временный файл и выполнить его сортировку в памяти. */

hFile = CreateFile(TempFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);

FsLow = GetFileSize(hFile, NULL);

hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, FsLow + TSIZE, NULL);

pFile = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0 /* FsLow + TSIZE */, 0);

qsort(pFile, FsLow / RECSIZE, RECSIZE, KeyCompare);

/* KeyCompare – как в программе 5.2. */

/* Отобразить отсортированный файл. */

pTFile = (LPTSTR)pFile;

pTFile[FsLow/TSIZE] = '\0';

_tprintf(_T("%s"), pFile);

UnmapViewOfFile(pFile);

CloseHandle(hMap);

CloseHandle(hFile);

DeleteFile(TempFile);

return 0;

}

Описанный вариант реализации довольно прост, однако возможен и другой вариант, не требующий использования отображения файлов. Для этого достаточно распределить память, считать весь файл, выполнить его сортировку в памяти и записать на диск. По своей эффективности это решение, которое приведено на Web-сайте книги, не уступает программе 5.4, а нередко и превосходит ее, как показано в приложении В.