POSIX environments provide at least two ways of accessing files. There's the standard system calls open()
, read()
, write()
, and friends, but there's also the option of using mmap()
to map the file into virtual memory.
When is it preferable to use one over the other? What're their individual advantages that merit including two interfaces?
The mmap() function is used for mapping between a process address space and either files or devices. When a file is mapped to a process address space, the file can be accessed like an array in the program.
In short, mmap() is great if you're doing a large amount of IO in terms of total bytes transferred; this is because it reduces the number of copies needed, and can significantly reduce the number of kernel entries needed for reading cached data.
Both methods are viable. mmap method is a little bit more restrictive then shmget , but easier to use. shmget is the old System V shared memory model and has the widest support. mmap / shm_open is the new POSIX way to do shared memory and is easier to use.
In my performance tests, I'm seeing that reading from memory mapped files is 30X faster than reading through regular c++ stdio.
mmap
is great if you have multiple processes accessing data in a read only fashion from the same file, which is common in the kind of server systems I write. mmap
allows all those processes to share the same physical memory pages, saving a lot of memory.
mmap
also allows the operating system to optimize paging operations. For example, consider two programs; program A
which reads in a 1MB
file into a buffer creating with malloc
, and program B which mmaps
the 1MB file into memory. If the operating system has to swap part of A
's memory out, it must write the contents of the buffer to swap before it can reuse the memory. In B
's case any unmodified mmap
'd pages can be reused immediately because the OS knows how to restore them from the existing file they were mmap
'd from. (The OS can detect which pages are unmodified by initially marking writable mmap
'd pages as read only and catching seg faults, similar to Copy on Write strategy).
mmap
is also useful for inter process communication. You can mmap
a file as read / write in the processes that need to communicate and then use synchronization primitives in the mmap'd
region (this is what the MAP_HASSEMAPHORE
flag is for).
One place mmap
can be awkward is if you need to work with very large files on a 32 bit machine. This is because mmap
has to find a contiguous block of addresses in your process's address space that is large enough to fit the entire range of the file being mapped. This can become a problem if your address space becomes fragmented, where you might have 2 GB of address space free, but no individual range of it can fit a 1 GB file mapping. In this case you may have to map the file in smaller chunks than you would like to make it fit.
Another potential awkwardness with mmap
as a replacement for read / write is that you have to start your mapping on offsets of the page size. If you just want to get some data at offset X
you will need to fixup that offset so it's compatible with mmap
.
And finally, read / write are the only way you can work with some types of files. mmap
can't be used on things like pipes and ttys.
One area where I found mmap() to not be an advantage was when reading small files (under 16K). The overhead of page faulting to read the whole file was very high compared with just doing a single read() system call. This is because the kernel can sometimes satisify a read entirely in your time slice, meaning your code doesn't switch away. With a page fault, it seemed more likely that another program would be scheduled, making the file operation have a higher latency.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With