Linux system programming - interprocess communication (mmap memory mapping)

Posted by davejj on Tue, 15 Feb 2022 15:38:04 +0100

The previous article introduces the common communication methods between processes: nameless pipeline and named pipeline. This article introduces memory mapping. Memory mapping is very convenient when multiple processes access files to read and write.

1. Introduction to memory mapping mmap function

The mmap function can map the files on the disk to the memory space and return the mapped first address.

Correlation function: mmap munmap msync

Function prototype and parameter introduction:

#include <unistd.h>
#include <sys/mman.h>
int msync(const void *start, size_t length, int flags);		
Function function: Synchronize changes made to the memory area to the file

void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
Function function: It is used to map the contents of a file into memory. The access to the memory area is the direct reading and writing of the contents of the file. This can speed up file access.
Return value: the starting address of the mapped memory is returned successfully.
(1)	First parameter start Point to the starting address of the memory to be mapped, which is usually set to NULL,It means that the system will automatically select the address. After the correspondence is successful, the address will be returned.
(2)	Second parameter length Represents how much of the file is mapped to memory.
(3)	Third parameter prot The protection methods representing the mapping area include the following combinations:
PROT_EXEC Mapped area
PROT_READ The mapping area can be read
PROT_WRITE The mapping area can be written
PROT_NONE The mapped area cannot be accessed
(4)	Fourth parameter flags Various properties that affect the mapping area:
MAP_FIXED  	If parameter start If the mapping of the address cannot be successfully established, the mapping will be abandoned and the address will not be corrected. This flag is generally discouraged.
MAP_SHARED  The data written to the mapped area will be copied back to the file and shared by other processes mapping the file.
MAP_PRIVATE Writing to the mapping area will result in a copy of the mapping file, that is, a private copy on write, Any changes made to this area will not be written back to the original file content.
MAP_ANONYMOUS 	Establish anonymous mapping. The parameter is ignored fd,No files are involved, and the mapping area cannot be shared with other processes.
MAP_DENYWRITE 	Only write operations to the mapped area are allowed, not to the mapped area fd Read and write to the file pointed to. Direct writing to the file will be rejected.
MAP_LOCKED  	Lock the mapping area, which means that the area will not be replaced( swap). 
In call mmap()Must be specified when MAP_SHARED or MAP_PRIVATE. 
    
(5)	Fifth parameter fd by open()The returned file descriptor represents the file to be mapped to memory.
(6)	Sixth parameter offset The offset mapped to the file is usually set to 0, which means that it corresponds from the front of the file, offset Must be an integer multiple of the page size.
    
Usage example:
fb_mem=mmap(NULL,smem_len,PROT_READ|PROT_WRITE,MAP_SHARED,fb,0);
    
int munmap(void *addr, size_t length);
Function function: Unmapping is used to unmap parameters start The starting address of the mapped memory, parameter length Is the memory size to cancel. When the process ends, the mapped memory will be released automatically, but it will not be released when the corresponding file descriptor is closed. Return value: 0 if unmapping is successful; otherwise, 0-1. 
Through memory mapping for process communication, multiple processes can map the same file to memory space at the same time. As long as one process modifies the file, other processes can get the modified data.
be careful: The permission to open the file must be consistent with the mapped permission!

2. Case code: mmap usage example (1)

The function of the following code: create a new file, set the file size, use the mmap function to map the file address, copy the address directly, enter the data, and then cancel the mapping. Then open the file again, and the data has been stored in the file. Demonstrates how to read and write files through mmap mapping file address.

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

int main(int argc,char **argv)
{
    if(argc!=2)
    {
        printf("./a.out <file>\n");
        return 0;
    }
    /*1. Create a file*/
    int fd;
    fd=open(argv[1],O_RDWR|O_CREAT,S_IRWXU);
    if(fd<0)
    {
        printf("%s File open failed.\n",argv[1]);
        return 0;
    }
    /*2. Set file size*/
    ftruncate(fd, 1024);
    /*3. Get file size*/
    struct stat s_buf;
    fstat(fd,&s_buf);
    printf("File size:%d Byte\n",s_buf.st_size);
    /*4. Mapping files to memory space*/
    unsigned char *mem_p=NULL;
    mem_p=mmap(NULL,s_buf.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(mem_p==NULL)
    {
        printf("File mapping failed.\n");
        close(fd);
        return 0;
    }
    /*5. Close file*/
    close(fd);
    /*6. Realize file reading and writing*/
    strcpy(mem_p,"mmap Function test.Realize file reading and writing.");
    /*7. Print the contents of the document*/
    printf("mem_p=%s\n",mem_p);
    /*8. Unmap*/
    munmap(mem_p,s_buf.st_size);
    return 0;
}

3. Case code: mmap usage example (2)

The function of the following code: when the program is running, pass in an existing file path from the command line, call open to open the file, map the file address through mmap, get the address, and then copy a string of string data into the file.

Note: when writing data through the address mapped by mmap, ensure that the range cannot exceed the size range of the file itself. It's wrong to go beyond this paragraph.

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

char buff[]="Memory mapping test";
int main(int argc,char **argv)
{
	if(argc!=2)
	{
		printf("./app <file>\n");
		return 0;
	}
	char *m_p;
	struct stat stat_buf;
	stat(argv[1],&stat_buf);
	int fd=open(argv[1],O_RDWR);
	m_p=mmap(0,stat_buf.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
	if(m_p!=NULL)printf("Mapping succeeded!\n");
	printf("m_p=%s\n",m_p);
	//memset(m_p,0,stat_buf.st_size);
	memcpy(m_p,buff,strlen(buff));
	munmap(m_p,stat_buf.st_size);
	close(fd);
	return 0;
}

4. Case code: multiple processes copy a large file concurrently

Code requirements: use the mmap function to map files to memory. memcpy()
Use multiple processes to copy a large file concurrently to consolidate the usage of mmap
Detailed requirements: create five sub processes and copy a file at the same time, and each process copies part of the file.
Set the size of the specified file: int truncate(const char *path, off_t length)

The copy structure is as follows:

Example code:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/wait.h>

//Define the number of child processes
#define FORK_NUMBER 4 

/*
Realize multi process concurrent copy of large files -- mmap
*/
int main(int argc,char **argv)
{
    if(argc!=3)
    {
        printf("./a.out <Copied source file> <new file>\n");
        return 0;
    }
    /*1. open the source file*/
    int src_fd=open(argv[1],O_RDWR);
    if(src_fd<0)
    {
        printf("%s Source file open failed.\n",argv[1]);
        return -1;
    }
    /*2. create a new file*/
    int new_fd=open(argv[2],O_RDWR|O_CREAT,S_IRUSR|S_IWUSR);
    if(new_fd<0)
    {
        printf("%s New file creation failed.\n",argv[2]);
        return -2;
    }
    /*3. Gets the size of the source file and sets the size of the new file*/
    struct stat s_buff;
    fstat(src_fd,&s_buff);
    printf("Byte size of the source file:%d\n",s_buff.st_size);
    ftruncate(new_fd,s_buff.st_size);
    /*4. Mapping source files to memory space*/
    unsigned char *src_p;
    src_p=mmap(NULL,s_buff.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,src_fd,0);
    if(src_p==NULL)
    {
        close(src_fd);
        printf("Source file mapping failed.\n");
        return -3;
    }
    /*5. Map new file to memory space*/
    unsigned char *new_p;
    new_p=mmap(NULL,s_buff.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,new_fd,0);
    if(new_p==NULL)
    {
        close(new_fd);
        printf("New file mapping failed.\n");
        return -4;
    }
    /*6. Close file*/
    close(new_fd);
    close(src_fd);
    /*7. Calculate the file byte size copied by the child process and the parent process*/
    int cp_size; 
    int main_size;
    cp_size=s_buff.st_size/FORK_NUMBER; //Size of each child process copy
    main_size=s_buff.st_size%FORK_NUMBER; //The size of the parent process copy
    /*8. Create child process*/
    int i;
    for(i=0;i<FORK_NUMBER;i++)
    {
        if(fork()==0)break;
    }
    /*9. The subprocess completes the copy of the file*/
    if(i<FORK_NUMBER) //Indicates that it is a child process
    {
        memcpy(new_p+i*cp_size,src_p+i*cp_size,cp_size);
        munmap(new_p,s_buff.st_size);
        munmap(src_p,s_buff.st_size);
    }
    else //Parent process
    {
        memcpy(new_p+i*cp_size,src_p+i*cp_size,main_size);
        munmap(new_p,s_buff.st_size);
        munmap(src_p,s_buff.st_size);
        pid_t pid;
        while(1)
        {
            pid=wait(NULL);
            if(pid==-1)break;
            printf("%d Subprocess exited successfully.\n",pid);
        }
        printf("The parent process exited successfully.\n");
    }
    return 0;
}

Topics: Linux Operation & Maintenance server