Как получить файл контрольных точек, используя mmap, который синхронизируется только с диском вручную - PullRequest
5 голосов
/ 30 июня 2010

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

Я думаю, что мне хотелось бы иметь файл mmap'd, который только синхронизируется с диском вручную.Я не уверен, как предотвратить автоматическую синхронизацию.

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

Ответы [ 5 ]

4 голосов
/ 30 июня 2010

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

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

2 голосов
/ 17 декабря 2010

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

A) Создайте новое отображение памяти, которое не копируется при записи, и скопируйте только те страницы, которые вы изменили в него.

Или

B) Откройте файл (обычное открытие файла) с прямым io (чтение и запись по размеру блока, выровненный по размеру) и пишите только те страницы, которые вы изменили. Direct io был бы хорош и быстр, потому что вы пишете целые страницы (размер страницы памяти кратен размеру блока диска) и нет буферизации. Этот метод выгоден тем, что не использует адресное пространство, если ваш mmap большой и нет места для mmap другого огромного файла.

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

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

- редактировать -

на самом деле, см. Можно ли найти грязную страницу в mmap из пространства пользователя? для идей о том, как увидеть, какие страницы грязные

2 голосов
/ 01 июля 2010

Как предположили другие респонденты, я не думаю, что есть портативный способ делать то, что вы хотите, без копирования.Если вы хотите сделать это в специальной среде, где вы можете управлять ОС и т. Д., Вы можете сделать это под Linux с файловой системой btrfs.

btrfs поддерживает новую операцию reflink()которая является существенной копией файловой системы при записи.Вы можете reflink() передать файл во временный режим при запуске, mmap() - временный, затем msync() и reflink() - временно вернуться к исходной точке проверки.

2 голосов
/ 30 июня 2010

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

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

Еще один очень стандартный подход, который работает до тех пор, пока ваши данные не настолько велики, чтобы 2 копии не помещались на диске: просто создайте вторую копию с временным именем, а затем rename() поверх старой. , rename() всегда атомарно. Это, вероятно, лучший подход, если у вас нет причин не делать этого таким образом.

0 голосов
/ 01 июля 2010

Я очень подозреваю, что ни одна ОС не может воспользоваться этим, но ОС могла бы заметить оптимизацию для:

int fd = open("file", O_RDWR | O_SYNC | O_DIRECT);

size_t length = get_lenght(fd);

uint8_t * map_addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);

...

// This represents all of the changes that could possibly happen before you
// want to update the on disk file.
change_various_data(map_addr);

if (is_time_to_update()) {
   write(fd, map_addr, length);
   lseek(fd, 0, SEEK_SET);
   // you could have just used pwrite here and not seeked
}

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

Затем при вызове write ОС может заметить, что запись была выровнена по блокам как в памяти, так и на диске, а затем заметить, что некоторые страницы памяти источника уже синхронизированы с этими.точные страницы файловой системы, на которые они были записаны, и записывают только те страницы, которые изменились.

С учетом всего сказанного, меня не удивит, еслиего оптимизация не выполняется ни одной ОС, и этот тип кода оказывается очень медленным и вызывает много записи на диск, когда вы вызываете «write».Было бы здорово, если бы этим воспользовались.

...