Когда я должен использовать mmap для доступа к файлам?

216

В средах POSIX предусмотрены как минимум два способа доступа к файлам. Там стандартная система вызывает open(), read(), write() и друзей, но есть также возможность использовать mmap() для сопоставления файла в виртуальную память.

Когда предпочтительнее использовать один над другим? Каковы их индивидуальные преимущества, в том числе два интерфейса?

Теги:
file-io
mmap
posix

5 ответов

235
Лучший ответ

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

mmap также позволяет операционной системе оптимизировать операции подкачки. Например, рассмотрите две программы; программа A, которая считывает файл 1 МБ в буфер, создавая с помощью malloc, и программу B, которая копирует 1 МБ файл в память. Если операционная система должна поменять часть памяти A, она должна записать содержимое буфера для свопинга, прежде чем он сможет повторно использовать память. В случае B любые немодифицированные страницы mmap'd могут быть повторно использованы сразу, потому что ОС знает, как их восстановить из существующего файла, из которого они были mmap'd. (ОС может определить, какие страницы не модифицированы, изначально отмечая записываемые mmap'd-страницы как только для чтения и ломая seg-ошибки, аналогичные стратегии Copy on Write).

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

Одно место mmap может быть неудобным, если вам нужно работать с очень большими файлами на 32-битной машине. Это связано с тем, что mmap должен найти непрерывный блок адресов в вашем адресном пространстве процесса, который достаточно велик, чтобы соответствовать всему диапазону отображаемого файла. Это может стать проблемой, если ваше адресное пространство будет фрагментировано, где у вас может быть свободное место на 2 ГБ адресного пространства, но ни один отдельный диапазон его не может соответствовать отображению файла 1 ГБ. В этом случае вам может понадобиться сопоставить файл в меньших фрагментах, чем вы хотели бы сделать его пригодным.

Другая потенциальная неловкость с mmap в качестве замены для чтения/записи заключается в том, что вы должны начать свое сопоставление с смещениями размера страницы. Если вы просто хотите получить некоторые данные со смещением X, вам нужно будет исправить это смещение, чтобы оно совместимо с mmap.

И, наконец, чтение/запись - это единственный способ, которым вы можете работать с некоторыми типами файлов. mmap не может использоваться на таких вещах, как трубы и ttys.

  • 9
    Можете ли вы использовать mmap () для файлов, которые растут? Или размер фиксирован в момент, когда вы выделяете память / файл mmap ()?
  • 26
    Когда вы делаете вызов mmap, вы должны указать размер. Так что, если вы хотите сделать что-то вроде хвостовой операции, это не очень подходит.
Показать ещё 4 комментария
55

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

  • 3
    +1 Я могу это подтвердить. Для небольших файлов это быстрее malloc часть памяти и сделать 1 read в него. Это позволяет иметь тот же код, который обрабатывает карты памяти, обрабатывает malloc'ed.
  • 32
    Это сказало, ваше оправдание для этого не правильно. Планировщик не имеет ничего общего с разницей. Разница заключается в доступе на запись к таблицам страниц, который представляет собой глобальную структуру ядра, содержащую информацию о том, какие процессы содержат какую страницу памяти и свои права доступа. Эта операция может быть очень дорогостоящей (это может привести к потере действия в строках кэша, может отменить TLB, таблица является глобальной, поэтому должна быть защищена от одновременного доступа и т. Д.). Вам нужен определенный размер карты, чтобы накладные расходы на доступ к read выше, чем накладные расходы на манипулирование виртуальной памятью.
Показать ещё 1 комментарий
38

mmap имеет то преимущество, когда у вас есть случайный доступ к большим файлам. Другим преимуществом является то, что вы обращаетесь к нему с операциями памяти (memcpy, арифметикой указателей), не беспокоясь о буферизации. Обычный ввод-вывод иногда может быть довольно сложным при использовании буферов, когда у вас есть структуры, большие, чем ваш буфер. Код для обработки, который часто трудно получить, часто бывает проще. Это говорит о том, что при работе с mmap есть определенные ловушки. Как уже упоминалось, mmap достаточно дорогостоящий, поэтому его следует использовать только для заданного размера (от машины к машине).

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

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

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

6

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

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

  • 13
    Обратите внимание, что mmap () не всегда быстрее, чем read (). Для последовательных чтений mmap () не даст вам ощутимых преимуществ - это основано на эмпирических и теоретических данных. Если вы мне не верите, напишите свой собственный тест.
  • 0
    Я могу привести цифры из нашего проекта, своего рода текстовый индекс для базы данных фраз. Индекс имеет размер в несколько гигабайт, а ключи хранятся в троичном дереве. Индекс все еще растет параллельно с доступом для чтения, доступ вне отображаемых частей осуществляется через pread . В Solaris 9 Sparc (V890) доступ к pread в 2–3 раза медленнее, чем memcpy из mmap. Но вы правы, что последовательный доступ не обязательно быстрее.
Показать ещё 1 комментарий
3

В дополнение к другим приятным ответам, цитата из системное программирование Linux, написанная экспертом Google Робертом Любом:

Преимущества mmap( )

Манипулирование файлами через mmap( ) имеет несколько преимуществ перед стандартные read( ) и write( ) системные вызовы. Среди них:

  • Чтение и запись в файл с отображением памяти позволяет избежать посторонняя копия, возникающая при использовании системы read( ) или write( )вызовы, где данные должны быть скопированы в буфер пользователя и из него.

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

  • Когда несколько процессов сопоставляют один и тот же объект в памяти, данные распределяются между всеми процессами. Только для чтения и совместного доступа к записи сопоставления разделяются полностью; частные записи, доступные для записи, имеют их еще не-COW (копировать-на-запись) страницы.

  • Поиск вокруг отображения включает тривиальные манипуляции с указателями. Системный вызов lseek( ) не требуется.

По этим причинам mmap( ) является разумным выбором для многих приложений.

Недостатки mmap( )

При использовании mmap( ) следует иметь в виду несколько моментов:

  • Отображения памяти всегда представляют собой целое число страниц. Таким образом, разница между размером файла резервной копии и целое число страниц "потеряно" в качестве незаполненного пространства. Для небольших файлов значительная часть картирования может быть потрачена впустую. Например, с 4 КБ страниц, 7-байтовое отображение отходов составляет 4 089 байт.

  • Отображения памяти должны вписываться в адресное пространство процесса. С 32-разрядным адресным пространством очень большое количество отображений различного размера может привести к фрагментации адресного пространства, что затрудняет найти большие свободные смежные области. Эта проблема, конечно, много менее очевидным с 64-разрядным адресным пространством.

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню