Mapping memory with mmap

A process begins life with a certain amount of memory mapped to the text (the code) and data segments of the program file, together with the shared libraries that it is linked with. It can allocate memory on its heap at runtime using malloc(3) and on the stack through locally scoped variables and memory allocated through alloca(3). It may also load libraries dynamically at runtime using dlopen(3). All of these mappings are taken care of by the kernel. However, a process can also manipulate its memory map in an explicit way using mmap(2):

void *mmap(void *addr, size_t length, int prot, int flags,
  int fd, off_t offset);

It maps length bytes of memory from the file with the descriptor fd, starting at offset in the file, and returns a pointer to the mapping, assuming it is successful. Since the underlying hardware works in pages, the length is rounded up to the nearest whole number of pages. The protection parameter, prot, is a combination of read, write, and execute permissions and the flags parameter contains at least MAP_SHARED or MAP_PRIVATE. There are many other flags, which are described in the man page.

There are many things you can do with mmap. Here are a few of them.

Using mmap to allocate private memory

You can use mmap to allocate an area of private memory by setting the MAP_ANONYMOUS flag and the fd file descriptor to -1. This is similar to allocating memory from the heap using malloc(3) except that the memory is page-aligned and in multiples of pages. The memory is allocated in the same area as that used for libraries. In fact, that area is referred to by some as the mmap area for this reason.

Anonymous mappings are better for large allocations because they do not pin down the heap with chunks of memory, which would make fragmentation more likely. Interestingly, you will find that malloc(3) (in glibc at least) stops allocating memory from the heap for requests over 128 KiB and uses mmap in this way, so in most cases, just using malloc is the right thing to do. The system will choose the best way of satisfying the request.

Using mmap to share memory

As we saw in Chapter 10, Learning About Processes and Threads, POSIX shared memory requires mmap to access the memory segment. In this case, you set the MAP_SHARED flag and use the file descriptor from shm_open():

int shm_fd;
char *shm_p;

shm_fd = shm_open("/myshm", O_CREAT | O_RDWR, 0666);
ftruncate(shm_fd, 65536);
shm_p = mmap(NULL, 65536, PROT_READ | PROT_WRITE,
  MAP_SHARED, shm_fd, 0);

Using mmap to access device memory

As I mentioned in Chapter 8, Introducing Device Drivers, it is possible for a driver to allow its device node to be mmaped and so share some of the device memory with an application. The exact implementation is dependent on the driver.

One example is the Linux frame buffer, /dev/fb0. The interface is defined in /usr/include/linux/fb.h, including an ioctl function to get the size of the display and the bits per pixel. You can then use mmap to ask the video driver to share the frame buffer with the application and read and write pixels:

int f;
int fb_size;
unsigned char *fb_mem;

f = open("/dev/fb0", O_RDWR);
/* Use ioctl FBIOGET_VSCREENINFO to find the display dimensions
  and calculate fb_size */
fb_mem = mmap(0, fb_size, PROT_READ | PROT_WRITE, MAP_SHARED, f, 0);
/* read and write pixels through pointer fb_mem */

A second example is the streaming video interface, Video 4 Linux, version 2, or V4L2, which is defined in /usr/include/linux/videodev2.h. Each video device has a node named /dev/videoN, starting with /dev/video0. There is an ioctl function to ask the driver to allocate a number of video buffers which you can mmap into user space. Then, it is just a question of cycling the buffers and filling or emptying them with video data, depending on whether you are playing back or capturing a video stream.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.79.33