[Linux operating system] Shared memory mapping (mmap) in Linux system programming

In Linux system programming, communication between processes is an important task. Shared memory mapping (mmap) is an efficient process communication method, which allows multiple processes to share the same memory area, thereby realizing data sharing and communication. This article will introduce the concept, principle, usage and precautions of shared memory mapping to help readers better understand and apply shared memory mapping.

insert image description here

1. The concept of shared memory mapping

Shared memory mapping is a technique for mapping a file or device into the virtual address space of a process. Through shared memory mapping, multiple processes can access the same physical memory area, thereby realizing data sharing and communication.

Key benefits of shared memory mapping include:

  • Efficiency : Shared storage mapping avoids data duplication and transmission, and improves the efficiency of data access.
  • Flexibility : Shared memory mappings can be used for different types of data, including files, devices, and anonymous memory.
  • Simplicity : Shared memory mapping is easy to use, and only a few system calls are needed to complete the mapping and unmapping operations.

2. The principle of shared memory mapping

The principle of shared memory mapping is to map a file or device to a contiguous memory area in the process's virtual address space. This memory area is called a shared memory segment. Multiple processes can share and communicate data by accessing shared memory segments.

The process of shared storage mapping consists of the following steps:

  1. Create or open a shared memory segment.
  2. Map the shared memory segment into the virtual address space of the process.
  3. Read and write data in the shared memory segment.
  4. Unmap the shared memory segment.

3. How to use shared memory mapping

3.1 mmap

3.1.1 Function prototype, parameters and return value

The prototype of the mmap function is as follows:

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

Parameter Description:

  • addr: Specify the starting address of the mapping, usually set to NULL, let the system automatically select the appropriate address.
  • length: Specifies the length of the map, in bytes.
  • prot: Specifies the protection mode of the mapped area, which can be a combination of the following values:
    • PROT_READ: readable
    • PROT_WRITE: writable
    • PROT_EXEC: executable
    • PROT_NONE: not accessible
  • flags: The flag specifying the mapping area, which can be a combination of the following values:
    • MAP_SHARED: Shared mapping, multiple processes can access the same mapping area
    • MAP_PRIVATE: private mapping, each process has its own independent mapping area
    • MAP_FIXED: The mapping area must start from the specified address, if the specified address is already occupied, the mapping will fail
    • MAP_ANONYMOUS: Creates an anonymous mapped area, not associated with a file
  • fd: Specifies the file descriptor to be mapped, if the MAP_ANONYMOUS flag is used, this parameter is -1.
  • offset: The offset of the specified file, usually set to 0.

return value:

  • On success, returns the starting address of the mapped region.
  • On failure, return MAP_FAILED.

3.1.2 Precautions

  1. Correct setting of parameters : The parameters of the mmap function include the starting address, mapping length, access rights, mapping methods, etc. These parameters need to be set correctly according to specific needs to ensure that the mapping behaves as expected.

  2. Error handling : The return value of the mmap function is the start address of the mapped area. If the mapping fails, the return value is MAP_FAILED. After calling the mmap function, you need to check whether the return value is MAP_FAILED, and determine the specific cause of the error according to the value of errno, and perform corresponding error handling.

  3. Memory alignment : The starting address of the mapped area returned by the mmap function is usually aligned according to the system's memory page size ( 4096 ). When using mapped regions, you need to ensure that you access them with the correct offset and length to avoid accessing out-of-bounds or unmapped memory.

  4. Unmapping : When a mapped area is no longer needed, the munmap function should be used to unmap the mapped area. After unmapping, the previously mapped memory region will no longer be accessible, and access to this region will result in a segmentation fault. Therefore, it is necessary to ensure that the timing and scope of unmapping are correct to avoid memory leaks or access to illegal memory.

  5. Synchronization of shared memory : If a shared memory area is created using the mmap function (using the MAP_SHARED flag), it needs to be synchronized between multiple processes to avoid race conditions and data inconsistencies. Mechanisms such as semaphores and mutexes can be used to achieve synchronization between processes.

  6. Synchronization of file mapping : If you use the mmap function to map a file into memory, you need to pay attention to the fact that the modification of the data in memory may not be written to the file immediately. You can use the msync function to synchronize the modified data to the file, or use the munmap function to automatically synchronize when unmapping.

  7. Security : You need to pay attention to security issues when using the mmap function, especially when mapping files. It is necessary to ensure that access to the mapped area is legal to avoid potential security holes, such as illegal memory access or code injection through the mapped area.

3.1.3 Function examples

Here is an example using the mmap function:

#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main() {
    
    
    int fd;
    char *shared_memory;

    // 打开文件
    fd = open("shared_memory", O_RDWR);
    if (fd == -1) {
    
    
        perror("open");
        return 1;
    }

    // 将文件映射到进程的虚拟地址空间中
    shared_memory = (char *)mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (shared_memory == MAP_FAILED) {
    
    
        perror("mmap");
        return 1;
    }

    // 读取共享内存段中的数据
    printf("Data read from shared memory: %s\n", shared_memory);

    // 解除文件的映射
    if (munmap(shared_memory, 1024) == -1) {
    
    
        perror("munmap");
        return 1;
    }

    // 关闭文件
    close(fd);

    return 0;
}

In this example, we open a file called "shared_memory" and map it into the virtual address space of the process. Then, we read the data in the shared memory segment and unmap the file.

Communication between two processes:

First, process A creates a shared memory segment and maps it into its own virtual address space:

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

int main() {
    
    
    int fd;
    char *shared_memory;
    const char *message = "Hello from Process A!";

    // 创建或打开一个文件
    fd = open("shared_memory", O_CREAT | O_RDWR, 0666);
    if (fd == -1) {
    
    
        perror("open");
        return 1;
    }

    // 调整文件的大小
    if (ftruncate(fd, 1024) == -1) {
    
    
        perror("ftruncate");
        return 1;
    }

    // 将文件映射到进程的虚拟地址空间中
    shared_memory = (char *)mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (shared_memory == MAP_FAILED) {
    
    
        perror("mmap");
        return 1;
    }

    // 写入数据到共享内存段
    strcpy(shared_memory, message);

    // 解除文件的映射
    if (munmap(shared_memory, 1024) == -1) {
    
    
        perror("munmap");
        return 1;
    }

    // 关闭文件
    close(fd);

    return 0;
}

Process B then opens the same shared memory segment and maps it into its own virtual address space:

#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main() {
    
    
    int fd;
    char *shared_memory;

    // 打开文件
    fd = open("shared_memory", O_RDWR);
    if (fd == -1) {
    
    
        perror("open");
        return 1;
    }

    // 将文件映射到进程的虚拟地址空间中
    shared_memory = (char *)mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (shared_memory == MAP_FAILED) {
    
    
        perror("mmap");
        return 1;
    }

    // 读取共享内存段中的数据
    printf("Data read from shared memory: %s\n", shared_memory);

    // 解除文件的映射
    if (munmap(shared_memory, 1024) == -1) {
    
    
        perror("munmap");
        return 1;
    }

    // 关闭文件
    close(fd);

    return 0;
}

In the above example, Process A creates a shared memory segment and writes the data "Hello from Process A!". Process B opens the same shared memory segment and reads the data written by process A.

3.2 munmap

3.2.1 Function prototype, parameters and return value

The munmap function is used to unmap a mapped region.

The function prototype is as follows:

int munmap(void *addr, size_t length);

parameter:

  1. addrparameter:

    • Points to the starting address to unmap.
    • Usually a pointer to the mapped region returned by the mmap function.
  2. lengthparameter:

    • The length to unmap.
    • Usually the length of the mapped area passed in through the mmap function.

return value:

The return value of the munmap function is 0 for success, -1 for failure, and errno is set to indicate the cause of the error.

3.2.2 Precautions

  • The addr parameter must be a valid starting address of the mapped region, otherwise unmapping will fail.
  • The length parameter must be the same as the length used in the original mapping, otherwise the unmapping will fail.
  • After unmapping, the previously mapped memory region will no longer be accessible, and access to this region will result in a segmentation fault.
  • Unmapping MAP_SHAREDmay cause unexpected results if the flag was previously used and other processes are still accessing the mapped region.
  • After unmapping, if MAP_ANONYMOUSthe flag was used before, and no pointer to the mapped area is saved, the mapped area will not be accessible again.

When using the munmap function, you need to ensure that the timing and range of unmapping are correct to avoid memory leaks or access to illegal memory.

3.2.3 Function examples

Here is an example of unmapping using the munmap function:

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>

int main() {
    
    
    int *data;
    size_t length = sizeof(int) * 10;

    // 使用mmap函数创建映射区域
    data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (data == MAP_FAILED) {
    
    
        perror("mmap");
        exit(1);
    }

    // 在映射区域中写入数据
    for (int i = 0; i < 10; i++) {
    
    
        data[i] = i;
    }

    // 解除映射
    if (munmap(data, length) == -1) {
    
    
        perror("munmap");
        exit(1);
    }

    // 尝试访问解除映射的内存区域
    printf("%d\n", data[0]);  // 该行代码将导致段错误

    return 0;
}

In this example, first use the mmap function to create a mapping area with a size of 10 ints and store it in a datapointer. Then, write the numbers from 0 to 9 into the mapped area through a loop. Next, use the munmap function to unmap the mapped region. Finally, an attempt is made to access the unmapped memory region, which results in a segfault.

4. Parent-child process mmap communication

4.1 The specific steps are as follows:

  1. The parent process creates a shared memory area. You can use the shm_open function to create a named shared memory object, or you can use the mmap function to map a file into memory.

  2. The parent process uses the fork function to create a child process.

  3. The child process can directly access the shared memory by inheriting the shared memory descriptor of the parent process or the address of the file mapping.

  4. Parent and child processes can communicate through shared memory, and a data structure can be defined in the shared memory, and the parent process and child process communicate by reading and writing the data structure.

  5. Parent and child processes need to use synchronization mechanisms (such as semaphores, mutexes, etc.) to ensure that access to shared memory is safe, avoiding race conditions and data inconsistencies.

  6. When the communication is completed, the parent process and the child process need to use the munmap function to unmap the shared memory to release resources.

4.2 Examples

Parent and child processes communicate through shared memory:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/wait.h>

typedef struct {
    
    
    int value;
} SharedData;

int main() {
    
    
    int fd = shm_open("/myshm", O_CREAT | O_RDWR, 0666);
    ftruncate(fd, sizeof(SharedData));

    SharedData* sharedData = mmap(NULL, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);

    pid_t pid = fork();
    if (pid == 0) {
    
    
        // 子进程
        printf("Child process: value = %d\n", sharedData->value);
        sharedData->value = 100;
        printf("Child process: value = %d\n", sharedData->value);
        exit(0);
    } else if (pid > 0) {
    
    
        // 父进程
        sharedData->value = 50;
        printf("Parent process: value = %d\n", sharedData->value);
        wait(NULL);
        printf("Parent process: value = %d\n", sharedData->value);

        munmap(sharedData, sizeof(SharedData));
        shm_unlink("/myshm");
    } else {
    
    
        perror("fork");
        exit(1);
    }

    return 0;
}

4.3 Code Explanation

In this example, first use the shm_open function to create a named shared memory object, and set its size to the size of the SharedData structure. Then, use the mmap function to map the shared memory into the memory, and obtain the address of the shared memory. Next, use the fork function to create a child process.

In the child process, first print the value in the shared memory, then modify it to 100, and print it again. In the parent process, first set the value in the shared memory to 50, and then wait for the child process to exit. Finally, print the value in the shared memory, then use the munmap function to unmap the shared memory, and use the shm_unlink function to delete the shared memory object.

5. mmap communication between non-blood relationship processes

5.1 The specific steps are as follows:

  1. To create a shared memory area, you can use the shm_open function to create a named shared memory object, or you can use the mmap function to map a file into memory.

  2. All processes that need to communicate use the shm_open or mmap function to open or map the same shared memory object.

  3. Processes communicate through shared memory, and a data structure can be defined in shared memory, and processes communicate by reading and writing the data structure.

  4. Processes need to use synchronization mechanisms (such as semaphores, mutexes, etc.) to ensure that access to shared memory is safe, avoiding race conditions and data inconsistencies.

  5. When the communication is completed, the process needs to use the munmap function to unmap the shared memory, and the shm_unlink function to delete the shared memory object.

5.2 Examples

Unrelated processes communicate through shared memory:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/wait.h>

typedef struct {
    
    
    int value;
} SharedData;

int main() {
    
    
    int fd = shm_open("/myshm", O_CREAT | O_RDWR, 0666);
    ftruncate(fd, sizeof(SharedData));

    SharedData* sharedData = mmap(NULL, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);

    pid_t pid = fork();
    if (pid == 0) {
    
    
        // 子进程
        printf("Child process: value = %d\n", sharedData->value);
        sharedData->value = 100;
        printf("Child process: value = %d\n", sharedData->value);
        exit(0);
    } else if (pid > 0) {
    
    
        // 父进程
        sharedData->value = 50;
        printf("Parent process: value = %d\n", sharedData->value);
        wait(NULL);
        printf("Parent process: value = %d\n", sharedData->value);

        munmap(sharedData, sizeof(SharedData));
        shm_unlink("/myshm");
    } else {
    
    
        perror("fork");
        exit(1);
    }

    return 0;
}

5.3 Code Explanation

This example is similar to the previous example of mmap communication between parent and child processes, but here we use named shared memory objects. Unrelated processes realize shared memory communication by opening or mapping the same named shared memory object. The other steps are the same as the previous example.

It should be noted that when communicating between unrelated processes, it is necessary to ensure that the order of creating and opening shared memory objects is consistent, and that the size of shared memory and the definition of data structures are consistent. In addition, appropriate synchronization mechanisms need to be used to ensure that access to shared memory is safe.

6. Conclusion

Shared memory is a mechanism for inter-process communication that allows multiple processes to access the same memory area for efficient data sharing. When using shared memory for inter-process communication, you need to pay attention to the following key points:

  1. Create shared memory: You can use the shm_open function to create a named shared memory object, or you can use the mmap function to map a file into memory. The size needs to be specified when creating shared memory.

  2. Access to shared memory: The process maps the shared memory to its own address space through the mmap function, so that it can directly read and write data in the shared memory. Care should be taken to ensure that the order in which shared memory objects are created and opened is consistent.

  3. Synchronization mechanism: Since multiple processes accessing shared memory at the same time may cause race conditions and data inconsistencies, appropriate synchronization mechanisms need to be used to ensure that access to shared memory is safe. Commonly used synchronization mechanisms include semaphores, mutexes, and condition variables.

  4. Unmap and delete shared memory: When the communication is completed, the process needs to use the munmap function to unmap the shared memory, and use the shm_unlink function to delete the shared memory object.

It should be noted that the use of shared memory for inter-process communication needs to consider the issues of synchronization and data consistency. In actual use, appropriate modifications and extensions are also required according to specific needs to ensure the correctness and reliability of communication.

Commonly used functions and system calls include:

  • shm_open: Create or open a named shared memory object.
  • ftruncate: Set the size of shared memory.
  • mmap: Map shared memory into the address space of the process.
  • munmap: Unmap the shared memory.
  • shm_unlink: delete the named shared memory object.

In addition, you can also use other synchronization mechanism functions, such as sem_init, sem_wait, sem_post, etc., to achieve synchronous access to shared memory.

Guess you like

Origin blog.csdn.net/Goforyouqp/article/details/132315057