Коли я повинен використовувати mmap для доступу до файлів?


276

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

Коли краще використовувати один над іншим? Які їх переваги, зокрема два інтерфейси?


16
Дивіться також mmap () проти блоків читання та цю публікацію Лінуса Торвальда, на яку посилається в одній з відповідей.
MvG

Відповіді:


299

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

mmapтакож дозволяє операційній системі оптимізувати операції пейджингу. Наприклад, розгляньте дві програми; програма, Aяка зчитує 1MBфайл у буфер, що створює malloc, і програма B, що mmapsмає 1MB файл в пам'ять. Якщо операційній системі доведеться замінити частину Aпам'яті, вона повинна записати вміст буфера для обміну, перш ніж вона зможе повторно використовувати пам'ять. У Bтакому випадку будь-які незмінені mmap'd-сторінки можуть бути використані негайно, оскільки ОС знає, як відновити їх із наявного файлу, з якого вони були mmap. (Операційна система може виявити, які сторінки немодифіковані, спочатку позначивши записувані mmap'd сторінки як лише для читання та виявивши помилки Seg , подібно до стратегії Copy on Write ).

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

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

Інша потенційна незграбність mmap, яка заміняє читання / запис, полягає в тому, що вам потрібно почати своє відображення на зміщеннях розміру сторінки. Якщо ви просто хочете отримати деякі дані при компенсації, Xвам потрібно буде виправити це зміщення, щоб воно було сумісним mmap.

І , нарешті, читання / запис є єдиним способом ви можете працювати з деякими типами файлів. mmapне може бути використаний для таких речей, як труби та ttys .


10
Чи можете ви використовувати mmap () для файлів, що ростуть? Або розмір фіксується в момент, коли ви виділяєте mmap () пам'ять / файл?
Джонатан Леффлер

29
Під час здійснення дзвінка mmap вам потрібно вказати розмір. Тож якщо ви хочете зробити щось на зразок операції з хвостом, це не дуже підходить.
Дон Нойфельд

5
Афаїк MAP_HASSEMAPHOREхарактерний для BSD.
Патрік Шлютер

6
@JonathanLeffler Безумовно, ви можете використовувати mmap () для файлів, що ростуть, але вам доведеться знову викликати mmap () з новим розміром, коли файл досягне межі місця, яке ви спочатку виділили. PosixMmapFile LevelDB - це хороший приклад. Але він перестав використовувати mmap з 1.15. Ви можете отримати стару версію від Github
baotiao

4
mmap також може бути корисним у випадку, якщо файл потрібно обробити декількома пропусками: вартість виділення сторінок віртуальної пам'яті сплачується лише один раз.
Консольний

69

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


4
+1 Я можу це підтвердити. Для невеликих файлів це швидше mallocстворити частину пам'яті та зробити 1 readв неї. Це дозволяє мати той самий код, який обробляє карти пам'яті, керовані malloc'ed.
Патрік Шлютер

35
Це сказало, ваше виправдання для цього не правильно. Планувальник взагалі не має нічого спільного з різницею. Різниця полягає в доступі до запису до таблиць сторінок, що є глобальною структурою ядра, що містить, які процеси утримують сторінку пам'яті та її права доступу. Ця операція може бути дуже дорогою (вона може недійсні лінії кешу, вона може через відключену TLB, таблиця є глобальною, тому вона повинна бути захищена від одночасного доступу тощо). Вам потрібен певний розмір карти, щоб накладні витрати readдоступу були вищими, ніж накладні маніпуляції з віртуальною пам'яттю.
Патрік Шлютер

1
@ PatrickSchlüter Добре, я розумію, що на початку mmap () є накладні витрати, що передбачає зміну таблиці сторінок. Скажімо, ми відображаємо 16K файлу в пам'ять. Для розміру сторінки 4K, mmapнеобхідно оновити 4 записи в таблиці сторінки. Але використання readдля копіювання в буфер розміром 16K також включає оновлення 4-х записів у таблиці сторінки, не кажучи вже про те, що потрібно скопіювати 16K в простір користувача addr. Тож чи могли б ви детальніше розібратися в відмінностях операцій на таблиці сторінок, і чим це дорожче mmap?
flow2k

45

mmapмає перевагу, коли у вас є випадковий доступ до великих файлів. Ще одна перевага полягає в тому, що ви отримуєте доступ до нього операціями з пам’яттю (memcpy, арифметикою вказівника), не турбуючись з буферизацією. Нормальний введення / виведення іноді може бути досить важким при використанні буферів, коли у вас будова більша, ніж у буфера. Код для обробки, який часто важко отримати правильним, mmap, як правило, простіше. Сказавши це, є певні пастки при роботі mmap. Як люди вже згадували, mmapналаштовувати досить дорого, тому його варто використовувати лише для заданого розміру (в залежності від машини до машини).

Що стосується чистого послідовного доступу до файлу, це також не завжди є кращим рішенням, хоча відповідний виклик madviseможе зменшити проблему.

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

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


30

Окрім інших приємних відповідей, цитата з системного програмування Linux, написана експертом Google Робертом Лавом:

Переваги mmap( )

Маніпулювання файлами через mmap( )має декілька переваг перед стандартними read( )та write( )системними дзвінками. Серед них:

  • Читання та запис у файл, нанесений на пам'ять, дозволяє уникнути сторонньої копії, яка виникає при використанні read( )або write( )системних викликів, де дані повинні бути скопійовані в буфер простору користувача.

  • Окрім будь-яких потенційних помилок сторінки, читання та запис у файл, відображений на пам'ять, не спричиняє накладних системних викликів чи контекстних комутаторів. Це так само просто, як отримати доступ до пам'яті.

  • Коли кілька процесів відображають один і той же об'єкт у пам'яті, дані поділяються між усіма процесами. Відображення для запису лише для читання та спільного користування спільно використовуються; приватні карти, що можна записати, мають спільний доступ до сторінок COW (копіювання на запис).

  • Огляд карти включає тривіальні маніпуляції вказівниками. У lseek( )системному дзвінку немає потреби .

З цих причин mmap( )є розумним вибором для багатьох додатків.

Недоліки mmap( )

Є кілька моментів, які слід пам’ятати при використанні mmap( ):

  • Відображення пам'яті - це завжди ціла кількість сторінок за розміром. Таким чином, різниця між розміром файлу резервної копії та цілим числом сторінок "витрачається" як неміцний простір. Для невеликих файлів значний відсоток відображення може бути витрачено на марна кількість. Наприклад, зі сторінками 4 Кб, 7-байтне відображення витрачає 4 089 байт.

  • Відображення пам'яті повинно вміщуватися в адресний простір процесу. За допомогою 32-бітного адресного простору дуже велика кількість відображень різного розміру може призвести до фрагментації адресного простору, що ускладнить пошук великих вільних суміжних областей. Ця проблема, звичайно, набагато менш очевидна при 64-бітному адресному просторі.

  • Є великі витрати на створення та підтримання відображень пам'яті та пов'язаних з ними структур даних всередині ядра. Цей накладний покрив, як правило, усувається шляхом усунення подвійної копії, згаданої в попередньому розділі, особливо для великих та часто доступних файлів.

З цієї причини переваги mmap( )найбільшою мірою реалізуються, коли відображений файл великий (і, таким чином, будь-який витрачений простір становить невеликий відсоток від загального відображення) або коли загальний розмір відображеного файлу рівномірно ділиться на розмір сторінки ( і, таким чином, немає витраченого місця).


13

Відображення пам'яті може мати величезну перевагу швидкості порівняно з традиційним IO. Це дозволяє операційній системі зчитувати дані з вихідного файлу після дотику сторінок у картографічному файлі пам'яті. Це працює, створюючи несправні сторінки, які ОС виявляє, а потім ОС автоматично завантажує відповідні дані з файлу.

Це працює так само, як механізм підкачки і зазвичай оптимізується для високошвидкісного вводу / виводу шляхом зчитування даних про межі та розміри сторінки системи (зазвичай 4 К) - розмір, до якого оптимізовано більшість кешів файлової системи.


15
Зауважте, що mmap () не завжди швидше, ніж read (). Для послідовного читання mmap () не дасть вам вимірної переваги - це засновано на емпіричних та теоретичних доказах. Якщо ви мені не вірите, напишіть власний тест.
Тім Купер

1
Я можу дати цифри, що надходять від нашого проекту, свого роду текстовий індекс для бази даних фраз. Індекс має кілька гігабайт великих розмірів, а клавіші тримаються у потрійному дереві. Індекс все ще зростає паралельно до доступу для читання, доступ за межами відображених частин здійснюється через pread. У програмі Solaris 9 Sparc (V890) доступ для попереднього читання в 2–3 рази повільніший, ніж memcpyз mmap. Але ви праві, що послідовний доступ не є обов'язково швидшим.
Патрік Шлютер

19
Лише трохи ніпеля. Він не працює як механізм підкачки, це механізм підкачки. Картографування файлу - це присвоєння області пам'яті файлу замість анонімного файлу свопу.
Патрік Шлютер

2

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

Брудні сторінки ядро ​​не можна скинути з оперативної пам’яті. Якщо є місце для заміни, то вони можуть бути підписані на тимчасовий підказок для заміни. Але це дорого, і в деяких системах, таких як невеликі вбудовані пристрої, які мають лише флеш-пам’ять, взагалі немає заміни. У цьому випадку буфер буде застряг в оперативній пам’яті до тих пір, поки процес не вийде або, можливо, поверне його назад madvise().

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

Для цього не потрібно більше ніж один процес використання картографічного файлу як переваги.


Чи не може ядро ​​скинути "брудну" сторінку mmap'd, записавши її вміст спочатку в базовий файл?
Джеремі Фріснер

2
Під час використання read()сторінки, з якими в кінцевому підсумку вводяться дані, не мають жодного відношення до файлу, з якого вони можуть виникнути. Тому їх не можна виписати, за винятком обміну місцями. Якщо файл є mmap()ed, а відображення записується (на відміну від лише для читання) та записується до нього, то це залежить від того, було MAP_SHAREDчи відображення MAP_PRIVATE. Спільне відображення може / потрібно записати у файл, але приватне не може бути.
TrentP
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.