首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >对mmap()返回感到困惑-两个不同的指针?

对mmap()返回感到困惑-两个不同的指针?
EN

Stack Overflow用户
提问于 2021-02-13 11:10:14
回答 2查看 428关注 0票数 1

目前,我正试图了解Linux中内存映射的工作原理(或者实际上是这样的),我将从操作系统概念中了解POSIX系统中共享内存的一个示例。这两个档案如下:

生产者文件

代码语言:javascript
复制
// 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;
}

消费者文件

代码语言:javascript
复制
// 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指向物理内存中的共享内存对象。这个物理内存只是在两个进程之间共享,方法是将它映射到它们的地址空间。但是,这需要指针指向物理内存中的相同地址,不是吗?还是指向虚拟内存(即进程的地址空间)?如果是这样的话,它本身是否指向物理内存?

谢谢!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-02-14 12:24:26

Producerconsumer是两个不同的进程,因此它们各自都有自己的虚拟内存空间。当您附加共享段时,您指定的内核没有首选位置(在未分配的虚拟地址空间中)来放置它,所以您通常会得到不同的指针,因为很可能这两个进程都有不同的内存映射。假设一个进程在地址A处分配了内存映射,而另一个进程的地址A被它所分配的其他东西占用(例如堆的动态内存,或者不同的共享库),很明显内核不能在相同(虚拟)地址范围(甚至重叠其他映射)中分配两个不同的东西,所以它使用不同的位置,并返回不同的指针。

不要担心,因为两个虚拟地址都映射到相同的虚拟地址(这是肯定的,因为它是一个共享内存段),指针最终指向相同的东西。内核通常不知道它将同一段放在不同的进程中的位置,它不需要这样做。这意味着,这是非常不可能的,你得到两个指针相等,相反,你的想法。

由于这些进程都不知道其他进程的虚拟地址空间,所以没有冲突,但从不将地址传递给要使用的另一个进程,因为虚拟地址空间中的地址在另一个不同进程的虚拟地址空间中绝对没有任何意义。

进程通常无法(也不可能)知道内核是如何分析内存和构建虚拟到物理所有地址的映射的。即使是内核本身(内核也运行在它的虚拟内存空间中,并且只处理映射的表--这些表位于内核的虚拟地址空间中,而不是在任何用户进程的虚拟地址空间中,所以这就是为什么内核能够更改映射,但其他进程不可能)。

进程不可能知道虚拟地址在实际内存中指向何处。这对于某些管理员通过设备进行处理是可能的(为此需要一个特殊的设备) /dev/mem。但是,如果你试图在没有实际映射的情况下查看,你会得到一个混乱的页面--一些属于一个过程,一些属于另一个过程,没有明显的结构,或者你也不知道这些页面的内容意味着什么。

还有一个设备/dev/kmem映射到内核虚拟地址空间,并允许进程(例如调试器)访问那里的映射表.但这是一个非常危险的弯曲,因为你可以很容易地通过调整你的系统。考虑到您通常不能停止内核(这将停止系统),或者如果您能够这样做,您将停止系统中应该运行的重要部分。

票数 1
EN

Stack Overflow用户

发布于 2021-02-13 11:20:39

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

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

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66184422

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档