Memory Mapping

The mmap() system call provides mapping in the virtual address space of the calling process that maps the files or devices into memory. This is of two types −
File mapping or File-backed mapping − This mapping maps the area of the process’ virtual memory to the files. This means reading or writing to those areas of memory causes the file to be read or written. This is the default mapping type.
Anonymous mapping − This mapping maps the area of the process’ virtual memory without backed by any file. The contents are initialized to zero. This mapping is similar to dynamic memory allocation (malloc()) and is used in some malloc() implementations for certain allocations.
The memory in one process mapping may be shared with mappings in other processes. This can be done in two ways −
When two processes map the same region of a file, they share the same pages of physical memory.
When two processes map the same region of a file, they share the same pages of physical memory.
If a child process is created, it inherits the parent’s mappings and these mappings refer to the same pages of physical memory as that of the parent. Upon any change of data in the child process, different pages would be created for the child process.
If a child process is created, it inherits the parent’s mappings and these mappings refer to the same pages of physical memory as that of the parent. Upon any change of data in the child process, different pages would be created for the child process.
When two or more processes share the same pages, each process can see the changes of the page contents made by other processes depending on the mapping type. The mapping type can be either private or shared −
Private Mapping (MAP_PRIVATE) − Modifications to the contents of this mapping are not visible to other processes and the mapping is not carried to the underlying file.
Shared Mapping (MAP_SHARED) − Modifications to the contents of this mapping are visible to other processes and mapping is carried to the underlying file.
#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
The above system call returns the starting address of the mapping on success or MAP_FAILED on error.
The virtual address addr, can be either user specified or generated by the kernel (upon passing addr as NULL). The field length indicated requires the size of mapping in bytes. The field prot indicates memory protection values such as PROT_NONE, PROT_READ, PROT_WRITE, PROT_EXEC meant for regions that may not be accessed, read, write or executed respectively. This value can be single (PROT_NONE) or can be ORd with any of the three flags (last 3). The field flags indicate mapping type either or MAP_PRIVATE or MAP_SHARED. The field ‘fd’ indicates the file descriptor identifying the file to be mapped and the field ‘offset’ implies the starting point of the file, if need to map the entire file, offset should be zero.
#include <sys/mman.h>

int munmap(void *addr, size_t length);
The above system call returns 0 on success or -1 on error.
The system call munmap, performs the unmapping of the already memory mapped region. The fields addr indicates the starting address of the mapping and the length indicates the size in bytes of the mapping to be unmapped. Usually, the mapping and unmapping would be for the entire mapped regions. If this has to be different, then it should be either shrinked or cut in two parts. If the addr doesn’t have any mappings this call would have no effect and the call returns 0 (success).
Let us consider an example −
Step 1 − Writie into file Alpha Numeric characters as shown below −
Step 2 − Map the file contents into memory using mmap() system call. This would return the start address after mapped into the memory.
Step 3 − Access the file contents using array notation (can also access with pointer notation) as doesn’t read expensive read() system call. Using memory mapping, avoid multiple copying between the user space, kernel space buffers and buffer cache.
Step 4 − Repeat reading the file contents until the user enters “-1” (signifies end of access).
Step 5 − Perform clean-up activities i.e., unmapping the mapped memory region (munmap()), closing the file and removing the file.
/* Filename: mmap_test.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
void write_mmap_sample_data();

int main() {
   struct stat mmapstat;
   char *data;
   int minbyteindex;
   int maxbyteindex;
   int offset;
   int fd;
   int unmapstatus;
   write_mmap_sample_data();
   if (stat("MMAP_DATA.txt", &mmapstat) == -1) {
      perror("stat failure");
      return 1;
   }
   
   if ((fd = open("MMAP_DATA.txt", O_RDONLY)) == -1) {
      perror("open failure");
      return 1;
   }
   data = mmap((caddr_t)0, mmapstat.st_size, PROT_READ, MAP_SHARED, fd, 0);
   
   if (data == (caddr_t)(-1)) {
      perror("mmap failure");
      return 1;
   }
   minbyteindex = 0;
   maxbyteindex = mmapstat.st_size - 1;
   
   do {
      printf("Enter -1 to quit or ");
      printf("enter a number between %d and %d: ", minbyteindex, maxbyteindex);
      scanf("%d",&offset);
      if ( (offset >= 0) && (offset <= maxbyteindex) )
      printf("Received char at %d is %c\n", offset, data[offset]);
      else if (offset != -1)
      printf("Received invalid index %d\n", offset);
   } while (offset != -1);
   unmapstatus = munmap(data, mmapstat.st_size);
   
   if (unmapstatus == -1) {
      perror("munmap failure");
      return 1;
   }
   close(fd);
   system("rm -f MMAP_DATA.txt");
   return 0;
}

void write_mmap_sample_data() {
   int fd;
   char ch;
   struct stat textfilestat;
   fd = open("MMAP_DATA.txt", O_CREAT|O_TRUNC|O_WRONLY, 0666);
   if (fd == -1) {
      perror("File open error ");
      return;
   }
   // Write A to Z
   ch = 'A';
   
   while (ch <= 'Z') {
      write(fd, &ch, sizeof(ch));
      ch++;
   }
   // Write 0 to 9
   ch = '0';
   
   while (ch <= '9') {
      write(fd, &ch, sizeof(ch));
      ch++;
   }
   // Write a to z
   ch = 'a';
   
   while (ch <= 'z') {
      write(fd, &ch, sizeof(ch));
      ch++;
   }
   close(fd);
   return;
}
Enter -1 to quit or enter a number between 0 and 61: 3 
Received char at 3 is D 
Enter -1 to quit or enter a number between 0 and 61: 28
Received char at 28 is 2 
Enter -1 to quit or enter a number between 0 and 61: 38 
Received char at 38 is c 
Enter -1 to quit or enter a number between 0 and 61: 59 
Received char at 59 is x 
Enter -1 to quit or enter a number between 0 and 61: 65 
Received invalid index 65 
Enter -1 to quit or enter a number between 0 and 61: -99 
Received invalid index -99 
Enter -1 to quit or enter a number between 0 and 61: -1