Linux programming: The use of mmap memory mapping (a type of memory sharing), mmap-the ultimate weapon for large file copying?

Preface

Following on from the previous article about the use of shared memory in Linux, here
we introduce another method of ipc communication, memory mapping, while the iron is hot. In fact, this is also considered a type of shared memory, but memory mapping maps a certain section of the file to the memory to achieve data communication between different processes.
Previous blog portal: https://blog.csdn.net/haohaohaihuai/article/details/106862138

Fundamental

Ordinary reading and writing files

After the process performs ordinary file reading and writing, it will enter the kernel after calling the read/write system call. The kernel begins to read and write the file. Assuming that the kernel is reading the file, the kernel first reads the file into the kernel buffer, and then writes the data in the kernel buffer. Copying to the user buffer actually copies the data twice, that is, first from the file -> kernel buffer, and then from the kernel buffer -> user page buffer.
Insert image description here

memory map

After using mmap to map fd, disk -> user page cache, there is only one copy. Reducing one copy can greatly increase performance. This technique is also called "zero_copy".
Insert image description here
mmap maps a continuous section of a file in the hard disk space into a continuous section of memory. The concept of "file" can be a device, a file that drives a device, or a disk file. Memory mapping is to make file reading and writing faster and more efficient. Of course, it can also be used as a method of IPC communication.
Insert image description here

Function definition and parameters

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

start: starting address, generally defaults to NULL
length: mapping length, generally set to the file length when mapping files prot
: memory area permissions, read, write, execute, etc.
flags: other auxiliary flags when doing mapping
fd file descriptor , set to -1 when non-file mapping
offset: offset, generally set to 0 when mapping files
prot explanation:
PROT_EXEC page content can be executed.
The contents of the PROT_READ page may be read.
PROT_WRITE page content can be written.
The contents of the PROT_NONE page may be inaccessible.
Then PROT_WRITE | PROT_WRITE means that it can be read and written.
Flags explanation:

  • MAP_SHARED: Share the mapping space with all other processes mapping this object. Writing to the shared area is equivalent to outputting to a file. The file is not actually updated until msync() or munmap() is called.
  • MAP_PRIVATE: Create a copy-on-write private mapping. Writing to the memory area will not affect the original file. This flag and the above flags are mutually exclusive, only one of them can be used.
  • MAP_DENYWRITE : This flag is ignored.
  • MAP_EXECUTABLE: Same as above
  • MAP_NORESERVE: Do not reserve swap space for this mapping. When swap space is reserved, the possibility of modifications to the mapped area is guaranteed. When swap space is not reserved and memory is insufficient, modifications to the mapped area will cause a segment violation signal.
  • MAP_LOCKED: Locks the pages in the mapping area to prevent the pages from being swapped out of memory.
  • MAP_GROWSDOWN: used for the stack to tell the kernel VM system that the mapping area can be expanded downward.
  • MAP_ANONYMOUS: Anonymous mapping, the mapping area is not associated with any file.
  • MAP_ANON: Another name for MAP_ANONYMOUS, no longer used.
  • MAP_FILE : compatibility flag, ignored.
  • MAP_32BIT: Place the mapping area in the lower 2GB of the process address space. It will be ignored when MAP_FIXED is specified. Currently this flag is only supported on x86-64 platforms.
  • MAP_FIXED: Use the specified mapping start address. If the memory area specified by the start and len parameters overlaps with the existing mapping space, the overlapping part will be discarded. If the specified starting address is not available, the operation will fail. And the starting address must fall on a page boundary. This option is very dangerous (in itself) as it forces the removal of pre-existing mappings, thus making it easy for a multi-threaded process to corrupt its own process address space.
  • MAP_POPULATE: Prepare page tables for file mapping through read-ahead. Subsequent accesses to the mapped area are not blocked by page violations.
  • MAP_NONBLOCK: Only meaningful when used with MAP_POPULATE. No read-ahead is performed, only page table entries are created for pages that already exist in memory.

Some flag combinations are mutually exclusive, such as MAP_SHARED and MAP_PRIVATE, and some can be used mixedly, such as MAP_SHARED
|MAP_LOCKED, MAP_SHARED|MAP_ANONYMOUS
, MAP_SHARED, MAP_PRIVATE, and MAP_ANONYMOUS. The combination can be roughly divided into the following modes:

  • Private file mapping:
    Multiple processes use the same physical memory page for initialization, but modifications to memory files by each process will not be shared, nor will they be reflected in the physical file.
  • Private anonymous mapping
    mmap will create a new mapping that is not shared by each process. This use is mainly used to allocate memory (malloc will call mmap to allocate large memory).
  • Shared file mapping:
    Multiple processes share the same physical memory space through virtual memory technology. Modifications to the memory files will be reflected in the actual physical files, which is also a mechanism of inter-process communication (IPC).
  • Shared anonymous mapping
    This mechanism does not use copy-on-write when forking. The parent and child processes completely share the same physical memory page, which also realizes parent-child process communication (IPC).

mmap returns 0 for success, failure for MAP_FAILED, and the error code is in errno:
EACCES : Access error
EAGAIN : The file has been locked, or too much memory has been locked
EBADF : fd is not a valid file descriptor
EINVAL : One or more Invalid parameter
ENFILE : The system limit for open files has been reached
ENODEV : The file system where the specified file is located does not support memory mapping
ENOMEM : Insufficient memory, or the process has exceeded the maximum number of memory mappings
EPERM : Insufficient permissions, the operation is not allowed
ETXTBSY : Written method to open the file, and specify the MAP_DENYWRITE flag
SIGSEGV : trying to write to the read-only area
SIGBUS : trying to access a memory area that does not belong to the process

Usage example

1.MAP_SHARED mode

#include <sys/mman.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main(int argc, char **argv)
{
    
    
    int fd;
    char *map_ptr;
    struct stat buff = {
    
    0};
    //打开文件
    fd = open(argv[1], O_RDWR);
    if(fd<0)
    {
    
    
        printf("open erro: %s\n", strerror(errno));
        close(fd);
        return -1;
    }
    //获取文件状态
    if (fstat(fd, &buff) < 0)
    {
    
    
        printf("file state error:%s\n", strerror(errno));
        close(fd);
        return -1;
    }
    //允许读写
    //允许其它进程访问此内存区域
    printf("mmap file size: %ld\n", buff.st_size);
    map_ptr = mmap(NULL, buff.st_size+10, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    if(map_ptr == MAP_FAILED)
    {
    
    
        printf("mmap erro: %s\n", strerror(errno));
        close(fd);
        return -1;
    }
    //使用映射区域
    //读
    printf("mmap content: %s\n", map_ptr);
    //写
    memcpy(map_ptr, "LLL",3);
    
    munmap(map_ptr, buff.st_size);
    close(fd);
    
    return 0;
}

Create a new file mmap_demo.txt in the project directory and write the characters "pineapple and jackfruit~" . Save as a UTF-8 encoded format file.
Insert image description here
Compiled file: gcc -o mmapwrite mmapwrite.c
The utf-8 encoding used here is that Chinese characters are generally 3 bytes in length. In this example, LLL is used to replace the first 3 bytes.
After executing the program, in MAP_SHARED file sharing mode, the content will be updated to the file.
Insert image description here

2.MAP_PRIVATE mode

The above example mapping code is slightly modified to establish private file mapping.

    //允许读写
    //允许其它进程访问此内存区域
    printf("mmap file size: %ld\n", buff.st_size);
    map_ptr = mmap(NULL, buff.st_size+10, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
    if(map_ptr == MAP_FAILED)
    {
    
    
        printf("mmap erro: %s\n", strerror(errno));
        close(fd);
        return -1;
    }
    //使用映射区域
    printf("mmap content1: %s\n", map_ptr);
    memcpy(map_ptr, "LLL",3);
    printf("mmap content2: %s\n", map_ptr);

Insert image description here
After using MAP_PRIVATE, other processes are not allowed to access this memory area. It can be seen that the reading process did not read the "LLL" character content, and only read the original file content. Only the mmwrite process can read the changed content, and the changed content will not be synchronized to the file.

3. Mmap large file copy example

#include <sys/mman.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main(int argc, char **argv)
{
    
    
    int fd,fd1;
    char *rmap_ptr,*wmap_ptr;
    struct stat buff = {
    
    0};
    //打开文件
    fd = open(argv[1], O_RDWR);
    if(fd<0)
    {
    
    
        printf("open src erro: %s\n", strerror(errno));
        close(fd);
        return -1;
    }
    //获取文件状态
    if (fstat(fd, &buff) < 0)
    {
    
    
        printf("file state error:%s\n", strerror(errno));
        close(fd);
        return -1;
    }
	//判断目标文件是否存在, 不存在则创建目标文件
    fd1 = open("./file_copy", O_RDWR | O_CREAT | O_EXCL, 0777);
	if(fd1<0)
    {
    
    
        printf("open dest file erro: %s\n", strerror(errno));
        close(fd);
        return -1;
    }
	//设置文件大小
	ftruncate(fd1, buff.st_size);

    //读文件映射,使用MAP_PRIVATE
    printf("mmap file size: %ld\n", buff.st_size);
    rmap_ptr = mmap(NULL, buff.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if(rmap_ptr == MAP_FAILED)
    {
    
    
        printf("rmmap erro: %s\n", strerror(errno));
    	close(fd1);
        close(fd);
        return -1;
    }

    //写文件映射,必须用MAP_SHARED,否则内容将不能更新到文件
    wmap_ptr = mmap(NULL, buff.st_size, PROT_WRITE, MAP_SHARED, fd1, 0);
    if(wmap_ptr == MAP_FAILED)
    {
    
    
        printf("wmmap erro: %s\n", strerror(errno));
    	close(fd1);
        close(fd);
        return -1;
    }

    //拷贝文件内容
	memcpy(wmap_ptr,rmap_ptr,buff.st_size);

    close(fd1);
	close(fd);

   //解除映射
    munmap(wmap_ptr, buff.st_size);
    munmap(rmap_ptr, buff.st_size);

    return 0;
}

The file wps-office_11.1.0.8392_amd64.deb that needs to be copied is 213113086 bytes in size.
Execute the program: ./mmwrite wps-office_11.1.0.8392_amd64.deb
to get the copied target file file_copy. Copy speed is very fast.
Insert image description here

Summary of use:
1. The size of this example file mmap_demo.txt is 19 bytes, and buff.st_size+10 bytes are mapped. However, mmap() must map in PAGE_SIZE units. The physical memory page unit is 4096 bytes, so a page of 4096 bytes is actually mapped.
at this time

  • Accessing bytes 0~19 can return the file operation content, and the changed content is updated to the file.
  • Accessing bytes 20~4096 returns 0, and the written content will not be updated to the file.
  • Accessing more than 4096 bytes will cause a program SIGSECV error.

2. Mmap can be used to achieve efficient large-scale data reading and writing. When disk space needs to be exchanged for memory space, mmap is very effective. Can be used for IPC communication when using MAP_SHARED.

3. After mmap, the file content is not loaded into the physical page, only the address space is allocated in the virtual memory. When the process accesses this address, it searches the page table and finds that the page corresponding to the virtual memory is not cached in the physical memory. A "page fault" occurs, which is handled by the kernel's page fault exception handler, and the corresponding content of the file is stored in the physical memory. Pages (4096) are loaded into physical memory.

Author: Feima Programmer.
Welcome to technical exchange: QQ: 255895056.
Please indicate the source for reprinting. If there is any inappropriateness, please correct me.

Guess you like

Origin blog.csdn.net/haohaohaihuai/article/details/107039410
Recommended