Возможна ли необязательная запись файлов с отображением памяти?

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

  • have one open for writing, and later decide not to save it
  • have open open for reading, and later decide to save it

Наше приложение использует доступный для записи отображаемый в память файл для сохранения файлов данных, но, поскольку пользователь может захотеть выйти без сохранения изменений, мы должны использовать временный файл, который пользователь фактически редактирует. Когда пользователь решает сохранить изменения, исходный файл перезаписывается временным файлом, поэтому он содержит последние изменения. Это обременительно, потому что файлы могут быть очень большими (> 1 ГБ) и их копирование занимает много времени.

Я пробовал много комбинаций флагов, используемых для создания сопоставления файлов, но ни одна из них, похоже, не обеспечивает гибкости сохранения по требованию. Кто-нибудь может подтвердить, что это так? Наше приложение написано на Delphi, но в нашем случае оно использует стандартный Windows API для создания сопоставления.

FMapHandle := CreateFileMapping(FFileHandle, nil, PAGE_READWRITE, 0, 2 * 65536, nil);
FBasePointer := MapViewOfFile(FileMapHandle, FILE_MAP_WRITE, FileOffsetHigh,
FileOffsetLow, NumBytes);

person Alan Clark    schedule 27.10.2009    source источник
comment
В файлах отображенных в память нет ничего особенного. Если вы пишете в файл — любой файл — то вы действительно записываете в него. Сохранение файла это запись в него. Если не хотите экономить, то не пишите.   -  person Rob Kennedy    schedule 27.10.2009
comment
Это не совсем так — вы можете писать в файл с отображением памяти без отражения изменений в базовом файле, в зависимости от флагов, с которыми он был создан.   -  person Alan Clark    schedule 27.10.2009


Ответы (2)


Я не думаю, что вы можете. Под этим я подразумеваю, что вы можете это сделать, но для меня это не имеет никакого смысла :-)

Весь смысл файла с отображением в память заключается в том, что это окно в реальный файл. Если вы не хотите, чтобы изменения отражались в файле, вам, вероятно, придется сделать что-то вроде объединения изменений в структуру данных (например, массива базового адреса, размера и данных) и применить их при сохранении.

В этом случае вам на самом деле не нужен файл с отображением памяти, просто прочитайте и сохраните фрагменты, которые вы хотите изменить (сначала заблокируйте файл, если есть возможность многопользовательского доступа).

Обновление:

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

Вам все равно придется копировать исходные данные во временный файл при загрузке, но вам не придется копировать временные данные обратно (независимо от того, сохраните вы их или нет) - это вдвое сократит время.

person paxdiablo    schedule 27.10.2009
comment
Я боялся этого! Я подумал, что, возможно, исключение FlushViewOfFile приведет к отклонению изменений. Увы, нет, это все равно пишется. - person Alan Clark; 27.10.2009
comment
Привет да, PaxDiablo, это именно то, что мы делаем. Я должен был упомянуть об этом в исходном посте. - person Alan Clark; 27.10.2009

Возможно, но нетривиально.

Вы должны понимать основы отображения памяти и разницу между тремя режимами отображения файлов памяти. Оба выделяют часть вашего виртуального адресного пространства и создают запись сопоставления во внутренней таблице. Физическая оперативная память изначально не выделяется. Следовательно, когда вы ДЕЙСТВИТЕЛЬНО пытаетесь получить доступ к памяти, происходит сбой ЦП, и ОС должна исправить это. Это делается путем копирования содержимого файла в ОЗУ и сопоставления ОЗУ с вашим процессом по ошибочному адресу.

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

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

Если пользователь решит сохранить, у вас будет немного более сложная задача. Теперь вам нужно выяснить, какие части файла изменились. Эти изменения находятся в памяти, и вам нужно повторно применить их к исходному файлу. Это можно сделать с помощью Page Guards. Итак, когда пользователь решит сохранить, скопируйте все измененные страницы в отдельный блок памяти, переназначьте (неизмененный) файл для записи и примените изменения.

person MSalters    schedule 30.10.2009
comment
Спасибо, я изучу его, хотя его очевидная сложность может перевесить его преимущества! - person Alan Clark; 02.11.2009
comment
На самом деле я бы не назвал это явно сложным. На самом деле идея pageguard состоит в том, чтобы просто создать список измененных указателей страниц. Копирование всех измененных страниц и применение изменений являются тривиальными операциями: for each (srcPage in set) { CopyMemory(newPage.base, srcPage.base, Page::size); } - person MSalters; 02.11.2009