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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • Что означает «#define _GNU_SOURCE»?
  • Получение наивысшего выделенного файлового дескриптора
  • Преобразование года и месяца (формат «yyyy-mm») на дату?
  • Как удалить файл со странным именем?
  • Что такое / dev / null 2> & 1?
  • Как открываются флаги O_SYNC и O_DIRECT (2) разные / одинаковые?
  • Как построить c ++ fstream из дескриптора файла POSIX?
  • Задача Cron для работы в последний день месяца
  • Как выполнить команду и получить вывод команды в C ++ с помощью POSIX?
  • Давайте будем гением компьютера.