Kapan saya harus menggunakan mmap untuk akses file?

276

Lingkungan POSIX menyediakan setidaknya dua cara untuk mengakses file. Ada panggilan sistem standar open(), read(), write(), dan teman-teman, tapi ada juga pilihan untuk menggunakan mmap()untuk memetakan file ke memori virtual.

Kapan lebih baik menggunakan yang satu dari yang lainnya? Apa keunggulan masing-masing yang pantas termasuk dua antarmuka?

Peter Burns
sumber
16
Lihat juga mmap () vs. blok bacaan dan pos ini oleh Linus Torvalds dirujuk dalam salah satu jawaban di sana.
MvG

Jawaban:

299

mmapsangat bagus jika Anda memiliki beberapa proses mengakses data dengan cara hanya baca dari file yang sama, yang umum dalam jenis sistem server yang saya tulis. mmapmemungkinkan semua proses untuk berbagi halaman memori fisik yang sama, menghemat banyak memori.

mmapjuga memungkinkan sistem operasi untuk mengoptimalkan operasi paging. Misalnya, pertimbangkan dua program; program Ayang membaca 1MBfile menjadi buffer malloc, dan program B yang mmapsfile 1MB menjadi memori. Jika sistem operasi harus menukar sebagian Amemori keluar, ia harus menulis konten buffer untuk menukar sebelum dapat menggunakan kembali memori. Dalam Bhal apapun mmaphalaman yang tidak dimodifikasi dapat digunakan kembali dengan segera karena OS tahu cara mengembalikannya dari file yang sudah ada mmap. (OS dapat mendeteksi halaman mana yang tidak dimodifikasi dengan awalnya menandai mmaphalaman yang dapat ditulis sebagai hanya baca dan menangkap kesalahan seg , mirip dengan strategi Copy on Write ).

mmapjuga berguna untuk komunikasi antar proses . Anda bisa mmapfile sebagai baca / tulis dalam proses yang perlu berkomunikasi dan kemudian menggunakan primitif sinkronisasi di mmap'dwilayah (ini adalah apa MAP_HASSEMAPHOREbendera untuk).

Satu tempat yang mmapbisa menjadi canggung adalah jika Anda perlu bekerja dengan file yang sangat besar pada mesin 32 bit. Ini karena mmapharus menemukan blok alamat yang berdekatan di ruang alamat proses Anda yang cukup besar untuk memenuhi seluruh rentang file yang dipetakan. Ini bisa menjadi masalah jika ruang alamat Anda menjadi terfragmentasi, di mana Anda mungkin memiliki ruang alamat 2 GB gratis, tetapi tidak ada rentang individu yang dapat memuat pemetaan file 1 GB. Dalam hal ini Anda mungkin harus memetakan file dalam potongan yang lebih kecil daripada yang Anda inginkan agar sesuai.

Kecanggihan potensial lain dengan mmapsebagai pengganti baca / tulis adalah bahwa Anda harus memulai pemetaan Anda pada offset ukuran halaman. Jika Anda hanya ingin mendapatkan beberapa data pada offset, XAnda harus memperbaiki offset itu sehingga kompatibel dengannya mmap.

Dan akhirnya, baca / tulis adalah satu-satunya cara Anda dapat bekerja dengan beberapa jenis file. mmaptidak dapat digunakan pada hal-hal seperti pipa dan ttys .

Don Neufeld
sumber
10
Bisakah Anda menggunakan mmap () pada file yang sedang tumbuh? Atau apakah ukurannya tetap pada titik ketika Anda mengalokasikan memori / file mmap ()?
Jonathan Leffler
29
Ketika Anda melakukan panggilan mmap, Anda harus menentukan ukuran. Jadi jika Anda ingin melakukan sesuatu seperti operasi ekor, itu sangat tidak cocok.
Don Neufeld
5
Afaik MAP_HASSEMAPHOREkhusus untuk BSD.
Patrick Schlüter
6
@JonathanLeffler Tentu saja Anda dapat menggunakan mmap () pada file yang sedang tumbuh, tetapi Anda harus memanggil mmap () lagi dengan ukuran baru ketika file mencapai batas ruang yang awalnya Anda alokasikan. Levelix's PosixMmapFile memberi Anda contoh yang baik. Tapi itu berhenti menggunakan mmap dari 1,15. Anda bisa mendapatkan versi lama dari Github
baotiao
4
mmap juga bisa berguna jika file perlu diproses dalam beberapa pass: biaya mengalokasikan halaman memori virtual hanya dibayar sekali.
Jib
69

Satu area di mana saya menemukan mmap () tidak menjadi keuntungan adalah ketika membaca file kecil (di bawah 16K). Overhead halaman salah untuk membaca seluruh file sangat tinggi dibandingkan dengan hanya melakukan panggilan sistem read () tunggal. Ini karena kernel kadang-kadang dapat membuat read sepenuhnya dalam slice waktu Anda, yang berarti kode Anda tidak beralih. Dengan kesalahan halaman, sepertinya lebih mungkin bahwa program lain akan dijadwalkan, membuat operasi file memiliki latensi yang lebih tinggi.

Ben Combee
sumber
4
+1 Saya dapat mengonfirmasi hal itu. Untuk file kecil, lebih cepat untuk mallocsepotong memori dan membuat 1 readke dalamnya. Ini memungkinkan untuk memiliki kode yang sama yang menangani peta memori menangani malloc'ed.
Patrick Schlüter
35
Ini mengatakan, pembenaran Anda untuk itu tidak benar. Penjadwal tidak ada hubungannya sama sekali dengan perbedaannya. Perbedaannya berasal dari akses tulis ke tabel halaman, yang merupakan struktur global dari kernel yang memegang proses apa yang memegang halaman memori mana dan hak aksesnya. Operasi ini bisa sangat mahal (bisa membuat jalur cache tidak valid, bisa melalui TLB jauh, tabelnya bersifat global sehingga harus dilindungi terhadap akses bersamaan, dll.). Anda memerlukan ukuran peta tertentu sehingga overhead readakses lebih tinggi daripada overhead manipulasi memori virtual.
Patrick Schlüter
1
@ PatrickSchlüter Oke, saya mengerti bahwa ada overhead pada awal mmap () yang melibatkan memodifikasi tabel halaman. Katakanlah kita memetakan 16 ribu file ke memori. Untuk ukuran halaman 4K, mmapharus memperbarui 4 entri dalam tabel halaman. Tetapi menggunakan readuntuk menyalin ke buffer 16K juga melibatkan memperbarui entri tabel 4 halaman, belum lagi perlu menyalin 16K ke dalam ruang addr pengguna. Jadi, bisakah Anda menguraikan perbedaan operasi pada tabel halaman, dan bagaimana harganya lebih mahal mmap?
flow2k
45

mmapmemiliki keuntungan ketika Anda memiliki akses acak pada file besar. Keuntungan lain adalah Anda mengaksesnya dengan operasi memori (memcpy, pointer aritmatika), tanpa repot dengan buffering. I / O yang normal kadang-kadang bisa sangat sulit ketika menggunakan buffer ketika Anda memiliki struktur yang lebih besar dari buffer Anda. Kode untuk menangani yang seringkali sulit untuk diperbaiki, mmap umumnya lebih mudah. Ini mengatakan, ada jebakan tertentu ketika bekerja dengan mmap. Seperti yang telah disebutkan orang, mmappengaturannya cukup mahal, sehingga layak digunakan hanya untuk ukuran tertentu (bervariasi dari mesin ke mesin).

Untuk akses berurutan murni ke file, itu juga tidak selalu merupakan solusi yang lebih baik, meskipun panggilan yang tepat untuk madvisedapat mengurangi masalah.

Anda harus berhati-hati dengan pembatasan perataan arsitektur Anda (SPARC, itanium), dengan baca / tulis IO buffer sering disejajarkan dengan benar dan tidak menjebak ketika mendereferensi pointer yang dicor.

Anda juga harus berhati-hati agar tidak mengakses di luar peta. Itu dapat dengan mudah terjadi jika Anda menggunakan fungsi string pada peta Anda, dan file Anda tidak mengandung \ 0 di bagian akhir. Ini akan berfungsi sebagian besar waktu ketika ukuran file Anda bukan kelipatan dari ukuran halaman karena halaman terakhir diisi dengan 0 (area yang dipetakan selalu dalam ukuran kelipatan dari ukuran halaman Anda).

Patrick Schlüter
sumber
30

Selain jawaban bagus lainnya, kutipan dari pemrograman sistem Linux ditulis oleh pakar Google Robert Love:

Keuntungan dari mmap( )

Memanipulasi file melalui mmap( )memiliki beberapa keunggulan dibandingkan panggilan standar read( )dan write( )sistem. Diantaranya adalah:

  • Membaca dari dan menulis ke file yang dipetakan memori menghindari salinan asing yang terjadi saat menggunakan read( )atau write( )panggilan sistem, di mana data harus disalin ke dan dari buffer ruang pengguna.

  • Selain dari kesalahan halaman yang potensial, membaca dari dan menulis ke file yang dipetakan dengan memori tidak menimbulkan panggilan sistem atau pengalihan konteks. Sesederhana mengakses memori.

  • Ketika beberapa proses memetakan objek yang sama ke dalam memori, data dibagi di antara semua proses. Pemetaan hanya-baca dan dibagikan yang dibagikan dibagikan secara keseluruhan; pemetaan pribadi yang dapat ditulis memiliki halaman yang belum-COW (copy-on-write) dibagikan.

  • Mencari di sekitar pemetaan melibatkan manipulasi pointer sepele. Tidak perlu untuk lseek( )panggilan sistem.

Untuk alasan ini, mmap( )merupakan pilihan cerdas untuk banyak aplikasi.

Kekurangan dari mmap( )

Ada beberapa hal yang perlu diingat ketika menggunakan mmap( ):

  • Pemetaan memori selalu merupakan jumlah bilangan bulat dalam ukuran. Dengan demikian, perbedaan antara ukuran file dukungan dan jumlah integer halaman "terbuang" sebagai ruang kendur. Untuk file kecil, persentase pemetaan yang signifikan mungkin terbuang sia-sia. Misalnya, dengan halaman 4 KB, pemetaan 7 byte menghabiskan 4.089 byte.

  • Pemetaan memori harus sesuai dengan ruang alamat proses. Dengan ruang alamat 32-bit, sejumlah besar pemetaan berbagai ukuran yang sangat besar dapat mengakibatkan fragmentasi ruang alamat, sehingga sulit untuk menemukan wilayah bersebelahan gratis yang besar. Masalah ini, tentu saja, jauh lebih jelas dengan ruang alamat 64-bit.

  • Ada overhead dalam membuat dan memelihara pemetaan memori dan struktur data terkait di dalam kernel. Overhead ini umumnya dihilangkan dengan menghilangkan salinan ganda yang disebutkan di bagian sebelumnya, terutama untuk file yang lebih besar dan sering diakses.

Untuk alasan ini, manfaat dari mmap( )yang paling terwujud ketika file yang dipetakan besar (dan dengan demikian ruang yang terbuang adalah persentase kecil dari total pemetaan), atau ketika ukuran total file yang dipetakan secara merata dibagi oleh ukuran halaman ( dan dengan demikian tidak ada ruang yang terbuang).

Miljen Mikic
sumber
13

Pemetaan memori memiliki potensi untuk keunggulan kecepatan yang sangat besar dibandingkan dengan IO tradisional. Ini memungkinkan sistem operasi membaca data dari file sumber saat halaman dalam file yang dipetakan memori disentuh. Ini berfungsi dengan membuat halaman yang salah, yang dideteksi OS dan kemudian OS memuat data terkait dari file secara otomatis.

Ini bekerja dengan cara yang sama seperti mekanisme paging dan biasanya dioptimalkan untuk I / O kecepatan tinggi dengan membaca data pada batas dan ukuran halaman sistem (biasanya 4K) - ukuran yang dioptimalkan untuk kebanyakan cache sistem file.

AndyG
sumber
15
Perhatikan bahwa mmap () tidak selalu lebih cepat dari baca (). Untuk bacaan berurutan, mmap () tidak akan memberi Anda keuntungan terukur - ini didasarkan pada bukti empiris dan teoretis. Jika Anda tidak percaya kepada saya, tulislah tes Anda sendiri.
Tim Cooper
1
Saya dapat memberikan angka yang berasal dari proyek kami, semacam indeks teks untuk basis data frase. Indeks beberapa Gigabyte besar dan kunci diadakan di pohon ternary. Indeks masih tumbuh secara paralel untuk membaca akses, akses di luar bagian yang dipetakan dibuat melalui pread. Pada Solaris 9 Sparc (V890) akses pread berada di antara 2 dan 3 kali lebih lambat memcpydari pada mmap. Tapi Anda benar bahwa akses berurutan tidak perlu lebih cepat.
Patrick Schlüter
19
Hanya sedikit nitpick. Itu tidak bekerja seperti mekanisme paging, itu adalah mekanisme paging. Memetakan file menetapkan area memori ke file alih-alih file swap anonim.
Patrick Schlüter
2

Keuntungan yang belum terdaftar adalah kemampuan mmap()untuk menjaga pemetaan hanya baca sebagai halaman bersih . Jika seseorang mengalokasikan buffer di ruang alamat proses, kemudian digunakan read()untuk mengisi buffer dari file, halaman memori yang sesuai dengan buffer itu sekarang kotor karena telah ditulis.

Halaman kotor tidak dapat dijatuhkan dari RAM oleh kernel. Jika ada ruang swap, maka mereka bisa keluar untuk bertukar. Tapi ini mahal dan pada beberapa sistem, seperti perangkat tertanam kecil dengan hanya memori flash, tidak ada swap sama sekali. Dalam hal ini, buffer akan terjebak dalam RAM sampai proses keluar, atau mungkin mengembalikannya madvise().

Non ditulis ke mmap()halaman bersih. Jika kernel membutuhkan RAM, ia dapat dengan mudah menjatuhkannya dan menggunakan RAM tempat halaman berada. Jika proses yang memetakan mengaksesnya lagi, itu menyebabkan kesalahan halaman kernel memuat kembali halaman-halaman dari file asalnya. . Cara yang sama mereka dihuni di tempat pertama.

Ini tidak memerlukan lebih dari satu proses menggunakan file yang dipetakan untuk menjadi keuntungan.

TrentP
sumber
Tidak bisakah kernel menjatuhkan halaman mmap'd yang 'kotor' dengan menulis isinya ke file yang mendasarinya terlebih dahulu?
Jeremy Friesner
2
Saat menggunakan read(), halaman-halaman yang datanya dimasukkan tidak memiliki hubungan dengan file asal mereka. Jadi mereka tidak bisa dituliskan, kecuali untuk menukar ruang. Jika suatu file mmap()ed, dan pemetaan dapat ditulis (bukan hanya baca), dan ditulis ke, maka itu tergantung pada apakah pemetaan itu MAP_SHAREDatau tidak MAP_PRIVATE. Pemetaan bersama bisa / harus ditulis ke file, tetapi pribadi tidak bisa.
TrentP