A simple mmap() readonly example

Problem:

You want to use mmap() from sys/stat.h POSIX header to map a file for reading (not writing). You can’t find any simple bare example on the internet.

Solution:

If you’re only interest in a high-level wrapper, please have a look at this boost::iostreams mmap example and the boost::iostreams mmap reference. Keep in mind, however, boost::iostreams does not provide all functionality mmap() does and boost::iostreams is quite a large dependency (you need to link the library!). However, it might be the best option for better portability to non-unix systems.

Here’s a minimal mmap() based cat implementation. It is not efficient, but demonstrates the important concepts. It uses the stat()-based getFilesize() function from this previous post

#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <assert.h>

size_t getFilesize(const char* filename) {
    struct stat st;
    stat(filename, &st);
    return st.st_size;
}

int main(int argc, char** argv) {
    size_t filesize = getFilesize(argv[1]);
    //Open file
    int fd = open(argv[1], O_RDONLY, 0);
    assert(fd != -1);
    //Execute mmap
    void* mmappedData = mmap(NULL, filesize, PROT_READ, MAP_PRIVATE | MAP_POPULATE, fd, 0);
    assert(mmappedData != MAP_FAILED);
    //Write the mmapped data to stdout (= FD #1)
    write(1, mmappedData, filesize);
    //Cleanup
    int rc = munmap(mmappedData, filesize);
    assert(rc == 0);
    close(fd);
}

 Step-by-step discussion

Step 1: Get the filesize. You need to know that before calling mmap, but you can also mmap a subset only.

Step 2: Open the file. Keep in mind you can’t use the fopen() function directly (you could use fileno() on the FILE object) because you need the file descriptor! In this case we’re using O_RDONLY because we want to mmap() read-only.

Step 3: Call mmap() with those parameters:

  • NULL: Let the kernel decide which address to mmap at. This is the recommended address parameter.
  • filesize: The size of the file. For readonly-mmap only values <= the real filesize make sense.
  • PROT_READ: Map the file read-only (in other words, use read-only page protection)
  • MAP_PRIVATE: Usually this means don’t write changes to the mmaped memory back to the file, but we mapped it readonly anyway, so using this or MAP_SHARED does’t make any noticeable difference.
  • MAP_POPULATE: Let the kernel preload parts of the file. This is a performance optimization. There is no absolute need to use this.
  • fd: The unix file descriptor to map (if you have a FILE*, you can get the descriptor number using fileno()). Must be opened * 0: The offset from the beginning of the file. 0 because we want to map the entire file

For a detailed mmap() reference, see the Opengroup page on mmap

Error-checking

The above implementation does not properly check for errors because it’s only a minimal example.

In any case, you should at least check if the value returned by mmap() is == MAP_FAILED. See the Opengroup page on mmap for details.

Update 2013/09/26: Close the file descriptor in cleanup section
Update 2016/01/07: assert != MAP_FAILED instead of NULL (Thanks to Erik Sjölund)