Быстрое изменение размера файла mmap - PullRequest
9 голосов
/ 02 января 2012

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

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

  1. mmap файл с MAP_PRIVATE
  2. делать доступ только для чтения к этой памяти в нескольких потоках
  3. либо получить мьютекс для файла, записать в память (предположим, это сделано таким образом, что читатели, которые могут читать эту память, не испортили его)
  4. или получите мьютекс, но увеличьте размер файла и используйте mremap, чтобы переместить его на новый адрес (измените размер отображения без копирования или ненужного ввода-вывода файла).

Сумасшедшая часть входит в (4). Если вы перемещаете память, старые адреса становятся недействительными, и читатели, которые все еще читают ее, могут внезапно иметь нарушение прав доступа. Что если мы изменим считыватели, чтобы перехватить это нарушение прав доступа, а затем перезапустить операцию (т.е. не перечитывать неверный адрес, пересчитать адрес с учетом смещения и новый базовый адрес из mremap.) Да, я знаю, что это зло , но, на мой взгляд, читатели могут только успешно прочитать данные по старому адресу или потерпеть неудачу с нарушением прав доступа и повторить попытку. Если будут приняты достаточные меры предосторожности, то должен быть безопасным. Поскольку изменение размера происходит не часто, читатели в конечном итоге преуспеют и не будут зацикливаться на повторных циклах.

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

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

Я прав? Может ли это работать? Есть ли в этом преимущество перед использованием двух сопоставлений MAP_SHARED?

1 Ответ

4 голосов
/ 02 января 2012

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

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

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

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

...