首页 > 解决方案 > 对 mmap() 返回感到困惑 - 两个不同的指针?

问题描述

目前试图了解内存映射在 Linux 中是如何工作的(或者一般来说,真的),我正在关注操作系统概念中 POSIX 系统中的共享内存的一个示例。这两个文件如下:

生产者档案

// This is the producer file for the Shared memory object

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

#include <sys/mman.h>

int main()
{
// ---------------- PRODUCER ESTABLISHES SHARED MEMORY OBJECT AND WRITES TO IT ----------------

    // Specifying the size in bytes of the shared memory object
    const int SIZE = 4096;
    
    // Name of the shared memory space
    const char *name = "OS";
    
    // The actual strings to write to shared memory
    const char *message_0 = "Hello";
    const char *message_1 = "World!";
    
    // Shared memory file descriptor will be stored in this
    int fd;
    
    // Pointer to the shared memory object will be stored in this
    char *ptr;
    
    // Checking error
    int errnum;
    
    // Create a shared memory object. This opens (establishes a connection to) a shared memory object.
    fd = shm_open(name, O_CREAT | O_RDWR,0666);
    
    if (fd == -1)
    {
        errnum = errno;
        fprintf(stdout, "Value of errno: %d\n", errno);
        perror("Error printed by perror");
        fprintf(stdout, "Error opening file: %s\n", strerror(errnum));
        return 1;
    }
    
    // Configure the size of the shared-memory object to be 4096 bytes
    ftruncate(fd, SIZE);
    
    // Memory map the shared memory object
    ptr = (char *) mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    
    printf("Ptr is: %p\n", ptr);
    
    if (ptr == MAP_FAILED)
    {
        errnum = errno;
        fprintf(stdout, "Value of errno in ptr: %d\n", errno);
        perror("Error printed by perror");
        fprintf(stdout, "Error opening file: %s\n", strerror(errnum));
        return 1;
    }
    
    // Write to the shared memory object
    sprintf(ptr, "%s", message_0);
    ptr += strlen(message_0);
    sprintf(ptr, "%s", message_1);
    ptr += strlen(message_1);
    
    return 0;
}

消费者档案

// This is the consumer file for the Shared memory object, in which it reads what is in the memory object OS

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

#include <sys/mman.h>

int main()
{
    // Size in bytes of shared memory object
    const int SIZE = 4096;
    
    // Name of the shared memory space
    const char *name = "OS";
    
    // Shared memory file descriptor will be stored in this
    int fd;
    
    // Pointer to the shared memory object will be stored in this
    char *ptr;
    
    // Checking error
    int errnum;
    
    // Open the shared memory object
    fd = shm_open(name, O_RDWR, 0666);
    
    // If error in shm_open()
    if (fd == -1)
    {
        errnum = errno;
        fprintf(stdout, "Value of errno: %d\n", errno);
        perror("Error printed by perror");
        fprintf(stdout, "Error opening file: %s\n", strerror(errnum));
        return 1;
    }
    
    
    // Memory-map the shared memory object
    ptr = (char *) mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    
    printf("Ptr is: %p\n", ptr);
    
    if (ptr == MAP_FAILED)
    {
        errnum = errno;
        fprintf(stdout, "Value of errno in ptr: %d\n", errno);
        perror("Error printed by perror");
        fprintf(stdout, "Error opening file: %s\n", strerror(errnum));
        return 1;
    }
    
    // Read from the shared memory object
    printf("%s\n", (char *) ptr);
            
    // Remove the shared memory object (delete it)
    shm_unlink(name);
    
    return 0;
}

当我打印指向共享内存对象 ( printf("Ptr value: %p\n, ptr)) 的指针时,我得到了消费者和生产者文件的两个不同值。为什么会这样?

据我了解,指针ptr指向物理内存中的共享内存对象。通过将其映射到它们的地址空间,该物理内存仅在两个进程之间共享。但是,这将要求指针指向物理内存中的相同地址,不是吗?还是指向虚拟内存(即进程的地址空间)?如果是这样,它本身是否指向物理内存?

谢谢!

标签: clinuxmemory

解决方案


每个进程都有自己的虚拟内存地址空间。通常,每个进程从虚拟内存到物理内存的映射是不同的。虽然共享内存可能位于地址 0x1000 的物理内存中,但生产者进程可能会将其映射到地址 0x7000 的虚拟内存中,而消费者进程可能会将其映射到地址 0x4000 的虚拟内存中。

此外,如果共享内存由于某种原因被换出内存,系统可以稍后将其重新加载到不同的物理地址,例如 0x13000,并更新进程中的映射,使其在每个进程中出现在与之前相同的地址生产者和消费者过程。


推荐阅读