Well, this is exactly what a memory mapped file is. And it's really easy to use, too. A few simple calls, mixed with a few simple rules, and you're mapping like a mad-person.
int fd; fd = open("mapdemofile", O_RDWR);
In this example, we've opened the file for read/write access. You can open it in whatever mode you want, but it has to match the mode specified in the prot parameter to the mmap() call, below.
To memory map a file, you use the mmap() system call, which is defined as follows:
void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);
What a slew of parameters! Here they are, one at a time:
As for return values, as you might have guessed, mmap() returns -1 on error, and sets errno. Otherwise, it returns a pointer to the start of the mapped data.
Anyway, without any further ado, we'll do a short demo that maps the second "page" of a file into memory. First we'll open() it to get the file descriptor, then we'll use getpagesize() to get the size of a virtual memory page and use this value for both the len and the off. In this way, we'll start mapping at the second page, and map for one page's length. (On my Linux box, the page size is 4K.)
#include <unistd.h> #include <sys/types.h> #include <sys/mman.h> int fd, pagesize; char *data; fd = fopen("foo", O_RDONLY); pagesize = getpagesize(); data = mmap((caddr_t)0, pagesize, PROT_READ, MAP_SHARED, fd, pagesize);
Once this code stretch has run, you can access the first byte of the mapped section of file using data[0]. Notice there's a lot of type conversion going on here. For instance, mmap() returns caddr_t, but we treat it as a char*. Well, the fact is that caddr_t usually is defined to be a char*, so everything's fine.
Also notice that we've mapped the file PROT_READ so we have
read-only access. Any attempt to write to the data (
int munmap(caddr_t addr, size_t len);
This simply unmaps the region pointed to by addr (returned from mmap()) with length len (same as the len passed to mmap()). munmap() returns -1 on error and sets the errno variable.
Once you've unmapped a file, any attempts to access the data through the old pointer will result in a segmentation fault. You have been warned!
A final note: the file will automatically unmap if your program exits, of course.
The program restricts the offsets you can specify to the range 0 through the file length. The file length is obtained through a call to stat() which you might not have seen before. It returns a structure full of file info, one field of which is the size in bytes. Easy enough.
Here is the source for mmapdemo.c:
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <sys/types.h> #include <sys/mman.h> #include <sys/stat.h> #include <errno.h> int main(int argc, char *argv[]) { int fd, offset; char *data; struct stat sbuf; if (argc != 2) { fprintf(stderr, "usage: mmapdemo offset\n"); exit(1); } if ((fd = open("mmapdemo.c", O_RDONLY)) == -1) { perror("open"); exit(1); } if (stat("mmapdemo.c", &sbuf) == -1) { perror("stat"); exit(1); } offset = atoi(argv[1]); if (offset < 0 || offset > sbuf.st_size-1) { fprintf(stderr, "mmapdemo: offset must be in the range 0-%d\n", \ sbuf.st_size-1); exit(1); } if ((data = mmap((caddr_t)0, sbuf.st_size, PROT_READ, MAP_SHARED, \ fd, 0)) == (caddr_t)(-1)) { perror("mmap"); exit(1); } printf("byte at offset %d is '%c'\n", offset, data[offset]); return 0; }
That's all there is to it. Compile that sucker up and run it with some command line like:
$ mmapdemo 30 byte at offset 30 is 'e'
I'll leave it up to you to write some really cool programs using this system call.
Copyright © 1997 by Brian "Beej" Hall. This guide may be reprinted in any medium provided that its content is not altered, it is presented in its entirety, and this copyright notice remains intact. Contact beej@ecst.csuchico.edu for more information.