• Linux- POSIX共享内存对象


    基本概念

    POSIX共享内存对象是一种允许多个进程共享一个给定名称的内存区域的机制。这些共享内存对象通常与POSIX信号量结合使用,以实现进程之间的同步。共享内存是IPC(进程间通信)机制中最快的方法之一,因为它允许进程直接访问同一块内存,而无需进行任何数据复制。

    以下是关于POSIX共享内存对象的一些主要特点和使用方式:

    1. 创建和打开共享内存对象

    使用shm_open函数创建新的共享内存对象或打开现有的对象。这个函数的行为与open系统调用类似。

    int shm_open(const char *name, int oflag, mode_t mode);
    
    • 1

    2. 设置共享内存对象的大小

    可以使用ftruncate函数设置共享内存对象的大小。

    int ftruncate(int fd, off_t length);
    
    • 1

    3. 映射共享内存对象到进程的地址空间

    使用mmap函数将共享内存对象映射到调用进程的地址空间。

    void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);
    
    • 1

    4. 解除映射

    可以使用munmap函数来解除共享内存对象的映射。

    int munmap(void *addr, size_t len);
    
    • 1

    5. 删除共享内存对象

    删除共享内存对象时,它不再可以被新的进程打开,但已经打开并映射到其地址空间的进程仍然可以访问它。使用shm_unlink函数来删除一个共享内存对象。

    int shm_unlink(const char *name);
    
    • 1

    示例
    两个进程可以使用上述机制来实现一个简单的共享计数器。

    1. 一个进程创建一个共享内存对象,并初始化一个在共享内存中的计数器。
    2. 另一个进程打开这个共享内存对象并递增计数器。
    3. 当两个进程都完成其工作后,它们都解除共享内存的映射。
    4. 最后,一个进程删除共享内存对象。

    注意

    • 这种机制适用于在同一台机器上的多个进程之间的通信。
    • 共享内存不提供任何同步机制;如果多个进程可能会同时读写共享内存,则需要使用其他机制(如信号量)来同步这些操作。
    • 使用共享内存时必须小心,因为错误的使用可能会导致数据损坏或不可预测的行为。

    shm_open()

    shm_open() 是一个POSIX函数,用于在POSIX共享内存对象之间创建或打开一个命名连接。这个函数使得多个进程可以共享一块内存,允许它们之间进行高效的数据交换或通信。

    共享内存通常比其他进程间通信(IPC)机制(如消息队列或管道)提供更高的性能,因为数据不需要在进程之间复制,而是直接在共享内存中进行读写。

    以下是 shm_open() 函数的基本参数和它们的描述:

    1. 名称 (name):

      • 共享内存对象的名称。这通常是以斜线(/)开始的一个字符串。多个进程可以使用相同的名称来引用相同的共享内存对象。
    2. 标志 (oflag):

      • 用于控制如何打开共享内存对象的标志。这些标志通常与 open() 系统调用中使用的标志相同,如 O_RDWR(读写模式)或 O_CREAT(如果共享内存对象不存在则创建)。
    3. 模式 (mode):

      • 如果创建了一个新的共享内存对象,此参数指定其权限。例如,S_IRUSR | S_IWUSR 允许文件的所有者读写访问。

    函数的基本原型如下:

    int shm_open(const char *name, int oflag, mode_t mode);
    
    • 1

    返回值:

    • 成功时,shm_open() 返回一个文件描述符,用于进一步的操作,例如使用 ftruncate() 设置共享内存大小或使用 mmap() 将其映射到进程的地址空间。
    • 失败时,返回 -1,并设置全局变量 errno 以指示错误原因。

    一些常见的使用场景和注意事项:

    • 大小设置: 使用 shm_open() 打开或创建的共享内存对象的初始大小为0。需要使用 ftruncate() 来设置适当的大小。

    • 映射: 为了在进程的地址空间中访问共享内存,需要使用 mmap() 函数将其映射到进程的地址空间。

    • 关闭和删除: 与共享内存相关的两个操作是 shm_unlink()(从系统中删除共享内存对象的名称)和 close()(关闭共享内存的文件描述符)。删除共享内存对象的名称并不意味着立即释放其资源;只有在没有进程映射这块内存时,系统才会真正释放其相关资源。

    总的来说,shm_open() 提供了一个高效、灵活的进程间通信机制,适用于需要大量数据交换或低延迟通信的应用。

    shm_unlink()

    shm_unlink() 是一个POSIX函数,用于删除一个命名的共享内存对象。当一个共享内存对象不再需要被多个进程访问时,通常使用此函数来删除其名称,从而释放相关的系统资源。

    以下是 shm_unlink() 的基本参数和描述:

    1. 名称 (name):
      • 要删除的共享内存对象的名称。这是与先前通过 shm_open() 创建或打开该对象时使用的名称相同的字符串。

    函数的基本原型如下:

    int shm_unlink(const char *name);
    
    • 1

    返回值:

    • 成功时,shm_unlink() 返回 0
    • 失败时,返回 -1,并设置全局变量 errno 以指示错误原因。

    一些常见的使用场景和注意事项:

    • 延迟删除:

      • 即使您使用 shm_unlink() 删除了共享内存对象的名称,只要还有进程映射或打开了这个对象,它在物理上仍然存在。只有在最后一个引用被关闭或取消映射后,系统才会真正释放其相关资源。
    • 避免资源泄漏:

      • 使用共享内存是一个强大的机制,但也容易导致资源泄漏。如果进程在映射共享内存后突然崩溃,并且名称没有被解除链接,那么这块内存会继续存在,直到系统重启或明确解除链接为止。因此,在使用共享内存时,需要确保在适当的时间解除链接。
    • 并发控制:

      • 在多个进程访问共享内存时,可能需要使用某种形式的同步,例如互斥锁或信号量,以确保数据的一致性和完整性。

    总的来说,shm_unlink() 是共享内存生命周期管理的一个重要部分,它使您可以在不再需要共享内存对象时释放相关资源。在设计使用共享内存的应用程序时,正确地使用它可以避免资源泄漏和系统资源的滥用。

    综合示例

    下面是一个示例,展示了如何使用shm_open(), shm_unlink(), mmap(), munmap(), ftruncate()等函数创建、使用和删除一个共享内存区域。在这个例子中,我们将创建两个进程:一个生产者和一个消费者,生产者向共享内存写入数据,消费者从共享内存读取数据。

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define SHM_NAME "/my_shared_memory"
    #define SHM_SIZE 4096
    
    int main() {
        int shm_fd;
        char *shared_memory;
    
        // Create or open a shared memory segment
        shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
        if (shm_fd == -1) {
            perror("shm_open");
            return 1;
        }
    
        // Set the size of the shared memory segment
        if (ftruncate(shm_fd, SHM_SIZE) == -1) {
            perror("ftruncate");
            return 1;
        }
    
        // Map the shared memory segment in the address space of the process
        shared_memory = mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
        if (shared_memory == MAP_FAILED) {
            perror("mmap");
            return 1;
        }
    
        pid_t pid = fork();
        if (pid == 0) { // Child - Consumer
            printf("Consumer reading...\n");
            while (strncmp(shared_memory, "exit", 4) != 0)
            {
                printf("Consumed: %s\n", shared_memory);
                sleep(2);
            }
        }
        else if (pid > 0) { // Parent - Producer
            char *messages[] = {
                "Hello from producer",
                "This is shared memory",
                "exit"};
    
            for (int i = 0; i < 3; i++) {
                strncpy(shared_memory, messages[i], SHM_SIZE - 1);
                printf("Produced: %s\n", messages[i]);
                sleep(1);
            }
    
            // Wait for child to finish
            wait(NULL);
    
            // Remove the shared memory segment
            if (shm_unlink(SHM_NAME) == -1) {
                perror("shm_unlink");
                return 1;
            }
        }
        else {
            perror("fork");
            return 1;
        }
    
        // Unmap the shared memory
        if (munmap(shared_memory, SHM_SIZE) == -1) {
            perror("munmap");
            return 1;
        }
    
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    这个例子包括以下步骤:

    1. 使用shm_open()创建或打开一个命名的共享内存段。
    2. 使用ftruncate()设置共享内存段的大小。
    3. 使用mmap()将共享内存段映射到进程的地址空间。
    4. 创建一个子进程。父进程(生产者)向共享内存写入消息,子进程(消费者)读取消息。
    5. 使用munmap()取消映射。
    6. 使用shm_unlink()删除共享内存段。
  • 相关阅读:
    Uniapp零基础开发学习笔记(11)-安装扩展组件uni-ui/uView及微信小程序开发环境
    JAVA NIO深入剖析
    数据结构与算法-砖墙问题
    【数据结构】链表的八种形态
    定点整数、小数
    基于Spark的数据清洗与转换
    显著性目标检测(一)——与图像分割、目标检测的区别
    EXPLAIN命令使用及功能介绍
    建木×GitLink,解锁高效开发新体验
    word 替换全部字母和数字为新罗马
  • 原文地址:https://blog.csdn.net/weixin_43844521/article/details/133254970