Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Создание эффективных приложений для Windows Джеффри Рихтер 2004 (Книга).pdf
Скачиваний:
377
Добавлен:
15.06.2014
Размер:
8.44 Mб
Скачать

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

Частичная передача физической памяти проецируемым файлам

До сих пор мы видели, что система требует передавать проецируемым файлам всю физическую память либо из файла данных на дигке, либо из страничного файла Это значит, что память используется не очень эффективно Давайте вспомним то, что я говорил в разделе "B какой момен! региону передают физическую память" главы 15 Допустим, Вы xoтитe сделать всю таблицу доступной другому процессу Если приме нить для этого механизм проецирования файлов, придется передать физическую па мять целой таблице

CELLDATA CellData[200][256];

Если структура CELLDATA занимает 128 байтов, показанный массив потребует 6 553 600 (200 x 256 x 128) байтов физической памяти Это слишком много — тем бо лее, что в таблице обычно заполняют всего несколько строк

Очевидно, что в данном случае, создав объект "проекцин файла", желательно не передавать ему заранее всю физическую память Функция CreateFtleMapping предус матривает такую возможность, для чего в параметр fdwProtect нужпо передать один из флагов SEC_RESRVE или SEC_COMMlT

Эти флаги имеют смысл, только если Вы создаете объект «проекция файла", ис пользующий физическую память из страничного файла. Флаг SEC_COMMIT заставля ет CreateFileMapping сразу же передать память из страничного файла. (То же самое происходит, если никаких флагов не указано.) Но когда Вы задаете флаг SEC_RESERVE, система не передает физическую память из страничного файла, а просто возвращает описатель объекта «проекция файла". Далее, вызвав MapViewOfFile или MapViewOfFileEx, можно создать представление этого объекта. При этом MapViewOfFile или MapView OfFileEx резервирует регион адресного пространства, не передавая ему физической памяти. Любая попытка обращения по одному из адресов зарезервированного регио на приведѐт к нарушению доступа

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

А теперь самое интересное. Оказывается, все, что нужно для передачи физической памяти общему (совместно используемому) региону, - вызвать функцию VirtualAlloc:

PVOID VirtualAlloc( PVOID pvAddress, SIZE_T dwSize, DWORD fdwAllocationType, DWORD friwProtect);

Эту функцию мы уже рассматривали (и очень подробно) в главе 15. Вызвать Virtual Alloc для передачи физической памяти представлению региона — то же самое, что вызвать VirtualAlloc для передачи памяти региону, ранее зарезервированному вызовом VirtualAlloc

с флагом MEM_RESERVE. Получается, что региону, зарезервированному функциями MapViewOfFile или MapViewOfFileEx, — как и региону, зарезервированно му функцией VirtualAlloc, — тоже можно передавать физическую память порциями, а не всю сразу. И если Вы поступаете именно так, учтите, что все процессы, спроеци ровавшие на этот регион представление одного и того же объекта «проекция файла», теперь тоже получат доступ к страницам физической памяти, переданным региону.

Итак, флаг SEC_RESERVE и функция VirtualAlloc позволяют сделать табличную мат рицу CellData «общедоступной" и эффективнее использовать память.

WINDOWS 98

Обычно VirtualAlloc не срабатывает, если Вы передаете ей адрес памяти, выхо дящий за пределы диапазона от 0x00400000 до 0x7FFFFFFF. Однако при перс даче физической памяти проецируемому файлу, созданному с флагом SEC_RE SERVE, в VirtualAlloc нужно передать адрес, укладывающийся в диапазон от 0x80000000 до 0xBFFFFFFE Только тогда Windows 98 поймет, что физическая память передается региону, зарезервированному под проецируемый файл, и даст благополучно выполнить вызов функции.

WINDOWS 2000

В Windows 2000 функция VirtualFree не годится для воврата физической па мяти, переданной в свос время проецируемому файлу (созданному с флагом SEC_RESERVE). Однако в Windows 98 такого ограничения нет.

Файловая система NTFS 5 поддерживает так называемые разреженные файлы (spar se files). Это потрясяющая новинка. Она позволяет легко создавать и использовать

разреженные проецируемые файлы (sparse memory-mapped files), которым физичес кая память предоставляется не из страничного, а из обычного дискового файла

Вот пример гого, как можно было бы воспользоваться этой новинкой Допустим, Вы хотите создать проецируемый в память файл (MMF) для записи аудиоданных При этом Вы должны записывать речь в виде цифровых аудиоданных в буфер памяти, связанный с дисковым файлом. Самый простой и эффективный способ решить эту задачу — применить разреженный MMF Все дело в том, что Вам заранее не известно, сколько времени будет говорить пользователь, прежде чем щелкнет кнопку Stop. Mo жет, пять минут, а может, пять часов — разница большая! Однако при использовании разреженного MMF это не проблема.

Программа-пример MMFSparse

Эта программа, «17 MMFSparseexe" (см листинг на рис. 17-4), демонстрирует, как создать проецируемый в память файл, связанный с разреженным файлом NTFS 5 Файлы исходного кода и ресурсов этой программы находятся в каталоге 17-MMFSparse на компакт-диске, прилагаемом к книге После запуска MMFSparse па экране появля ется окно, показанное ниже.

Когда Вы щелкнсте кнопку Create а 1 MB (1024 KB) Sparse MMF, программа попы тается создать разреженный файл «C:\MMFSpanse». Если Ваш диск С не является томом NTFS 5, у программы ничего не получится, и ее процесс завершится А если Вы созда ли том NTFS 5 на каком-то другом диске, модифицируйте мою программу и переком пилируйте ее, чтобы посмотреть, как она работает

После создания разреженный файл проецируется на адресное пространство про цесса. В поле Allocated Kangcs (внизу окна) показывается, какие части файла действи тельно связаны с дисковой памятью. Изначально файл не связан ни с какой памятью, и в этом поле сообщается «No allocated ranges in the file» («В файле нет выделенных диапазонов»).

Чтобы считать байт, просто введите число в поле Offset и щелкните кнопку Read Byte. Введенное Вами число умножается на 1024 (1 Кб), и программа, считав байт по полученному адресу, выводит его значение в поле Byte Если адрес попадает в область, не связанную с физической памятью, в этом поле всегда показывается нулевой байт.

Для записи байта введите число в поле Offset, a значение байта (0-255) — в поле Byte. Потом, когда Вы щелкнете кнопку Wrice Byte, смещение будет умножено на 1024, и байт по соответствующему адресу получит новое значение Операция записи мо жет заставить файловую систему передать физическую память какой-либо части фай ла Содержимое поля Allocated Ranges обновляется после каждой операции чтения или записи, показывая, какие части файла связаны с физической памятью на данный мо мент. Вот как вьплядит окно программы после записи всего одного байта по смеще нию 1 024 000 (1000 x 1024).

На этой иллюстрации видно, что физическая память выделена только одномуди апазону адресов — размером 65 536 байтов, начиняя с логического смещения 983 040 от начала файла С помощью ExpIorer Вы можете просмотреть свойства файла C:\MMFSparbe, как показано ниже.

Заметьте: на этой странице свойств сообщается, что длина файла равна 1 Мб (это виртуальный размер фаЙла), по на деле он занимает на диске только 64 Кб.

Последняя кнопка, Free All Allocated Regions, заставляет программу высвободить всю физическую память, выделенную для файла; таким образом, соответствующее дисковое пространство освобождается, а все байты в файле обнуляются.

Теперь поговорим о том, как работает эта программа. Чтобы упростить ее исход ный код, я создал С++-класс CSparseStream (который содержится в файле Sparse Stream.h) Этот

класс инкапсулирует поддержку операций с разреженным файлом или потоком данных

(stream). В файле MMFSparse.cpp я создал другой С++-класс, CMMFSparse, производный от CSparseStream. Так что объект класса CMMFSparse обла дает не только функциональностью CSparseStream, но и дополнительной, необходи мой для использования разреженного потока данных как проецируемого в память файла. В процессе создается единственный глобальный экземпляр класса CMMF Sparse — переменная g_mmf. Манипулируя разреженным проецируемым файлом, про грамма часто ссылается на эту глобальную переменную.

Когда пользователь щелкает кнопку Create а 1MB (1024 KB) Sparse MMF, програм ма вызывает CreateFile для создания нового файла в дисковом разделе NTFS 5. Пока что это обычный, самый заурядный файл Но потом я вызываю метод Initialize гло бального объекта g_mmf, передавая ему описатель и максимальный размер файла (1 Мб). Метод Initialize в свою очередь обращается к CreateFileMapping и создает объект ядра «проекция файла» указанного размера, а затем вызывает MapViewOfFile, чтобы сделать разреженный файл видимым в адресном пространстве данного процесса

Когда Initialize возвращает управление, вызывается функция Dlg_ShowAllocated Ranges Используя Windows-функции, она перечисляет диапазоны логических адре сов в разреженном файле, которым передана физическая память. Начальное смеще ние и длина каждого такого диапазона показываются в нижнем поле диалогового окна В момент инициализации объекта g_mmf файлу на диске еще не выделена физичес кая память, и данное поле отражает этот факт.

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

При чтении объекта g_mmf возвращается либо реальное значение байта, если дан ному диапазону адресов передана физическая память, либо 0, если память не передана,

Моя программа также демонстрирует, как вернуть файл в исходное состояние, высвободив все выделенные сму диапазоны адресов (после этого он фактически не занимает места ня диске) Реализуется это так. Пользователь щелкает кнопку Free All Allocated Regions. Однако освободить все диапазоны адресов, выделенные файлу, ко торый проецируется в память, нельзя Поэтому первое, что делает программа, — вы зывает метод ForceClose объекта g_mmf Этот метод обращается к UnmapViewOfFile, а потом — к CloseHandle, передавая описатель объекта ядра «проекция файла».

Далее вызывается метод DecommitPortionOfStream, который освобождает всю па мять, выделенную логическим байтам в файле. Наконец, программа вновь обращает ся к методу Initialize объекча g_mmf, и тот повторно инициализирует файл, проеци руемый на адресное пространство данного процесса. Чтобы подтвердить освобожде ние всей выделенной памяти, программа вызывает функцию Dlg_ShowAllocatedRanges которая выводит в поле строку «No allocated ranges in the file».

И последнее. Используя разреженный проецируемый файл в реальном приложе нии, Вы, наверное, захотите при закрытии файла урезать его логический размер до фактического Отсечение концевой части разреженного файла, содержащей нулевые