简体   繁体   English

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

[英]Confused about mmap() return - two different pointers?

Currently trying to understand how memory mapping works in Linux (or in general, really), and I'm following with this one example of Shared Memory in POSIX systems from Operating System Concepts.目前试图了解 memory 映射如何在 Linux (或一般来说,真的)中工作,我正在关注 POS 系统中的共享 Memory 的一个示例。 The two files are as follows:这两个文件如下:

Producer file生产者档案

// 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;
}

Consumer file消费者档案

// 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;
}

When I print the pointer to the shared memory object ( printf("Ptr value: %p\n, ptr) ), I get two different values for the consumer and producer files. Why does this happen?当我打印指向共享 memory object ( printf("Ptr value: %p\n, ptr) ) 的指针时,我得到了消费者和生产者文件的两个不同值。为什么会发生这种情况?

As I understand, the pointer ptr points at the shared memory object, which is in the physical memory.据我了解,指针ptr指向共享的 memory object,它位于物理 memory 中。 This physical memory is just shared amongst two processes, by mapping it onto their address space.这个物理 memory 只是在两个进程之间共享,通过将其映射到它们的地址空间。 However, this would require that the pointer point to the same address in physical memory, no?但是,这需要指针指向物理 memory 中的相同地址,不是吗? Or is it pointing to the virtual memory (ie the address space of the processes)?还是指向虚拟 memory(即进程的地址空间)? If so, does that itself point to the physical memory?如果是这样,它本身是否指向物理 memory?

Thanks!谢谢!

Each process has its own virtual memory address space.每个进程都有自己的虚拟 memory 地址空间。 The mappings from virtual memory to physical memory are, in general, different for each process.从虚拟 memory 到物理 memory 的映射通常对于每个进程是不同的。 While the shared memory may be in physical memory at address 0x1000, the producer process may have it mapped into virtual memory at address 0x7000, and the consumer process may have it mapped into virtual memory at address 0x4000. While the shared memory may be in physical memory at address 0x1000, the producer process may have it mapped into virtual memory at address 0x7000, and the consumer process may have it mapped into virtual memory at address 0x4000.

Further, if the shared memory is swapped out of memory for some reason, the system could later reload it to a different physical address, say 0x13000, and update the mappings in the processes so that it appears at the same addresses as before in each of the producer and consumer processes.此外,如果共享的 memory 出于某种原因从 memory 中换出,系统可以稍后将其重新加载到不同的物理地址,例如 0x13000,并更新进程中的映射,使其在每个进程中出现在与之前相同的地址生产者和消费者过程。

Producer and consumer are two different processes, so they have their own virtual memory space each. Producerconsumer是两个不同的进程,因此它们各自拥有自己的虚拟 memory 空间。 When you attach the shared segment, you specify the kernel that you have no preference on where (in your unallocated virtual address space) to put it, so you normally get different pointers because most probably both processes have different memory maps.当您附加共享段时,您指定 kernel ,您对放置它的位置(在未分配的虚拟地址空间中)没有偏好,因此您通常会得到不同的指针,因为很可能两个进程都有不同的 memory 映射。 Just think on an scenario where one process has allocated the memory map at, say, address A, and the other process has that address A occupied by some other thing it has allocated (eg dynamic memory for the heap, or a different shared library) It is clear that the kernel cannot allocate two different things in the same (virtual) address range (or even overlap some other mapping), so it uses a differen place, and returns a different pointer.试想一下,一个进程在地址 A 处分配了 memory map,而另一个进程的地址 A 被它分配的其他东西占用(例如,动态 ZCD69B4957F06CD818D7BF3D61980E2)很明显,kernel 不能在同一个(虚拟)地址范围内分配两个不同的东西(甚至重叠一些其他映射),所以它使用不同的位置,并返回不同的指针。

Just don't worry about it, as both virtual addresses map to the same phisical address (this is sure, as it is a shared memory segment), and the pointers finally end pointing to the same thing.不用担心,因为两个虚拟地址 map 都指向相同的物理地址(这是肯定的,因为它是共享的 memory 段),并且指针最终指向同一事物。 The kernel is normally not aware of where it has placed that same segment in a different process, it doesn't need to do. kernel 通常不知道将同一段放置在不同进程中的位置,它不需要这样做。 This means that it is highly improbable that you get both pointers equal, contrary to what you though.这意味着您极不可能使两个指针相等,这与您的想法相反。

As none of these processes knows about the virtual address space of the other, there's no conflict, but never pass the address to the other process to use, because an address in a virtual address space has absolutely no meaning in the virtual address space of another, different process.由于这些进程都不知道另一个进程的虚拟地址空间,所以没有冲突,但永远不要将地址传递给另一个进程使用,因为虚拟地址空间中的地址在另一个虚拟地址空间中绝对没有意义,不同的过程。

A process has normally no means (and no possibility) to know how the kernel assings the memory and builds the map of virtual to physicall addresses.进程通常没有办法(也没有可能)知道 kernel 如何分配 memory 并构建虚拟地址到物理地址的 map。 Even for itself (the kernel also runs in it's virtual memory space, and only deals with the tables that map ---these tables are in the virtual address space of the kernel, but not in the virtual address space of any user process, so this is the reason that makes the kernel capable of changing the mappings but it is not possible for other processes) Even for itself (the kernel also runs in it's virtual memory space, and only deals with the tables that map ---these tables are in the virtual address space of the kernel, but not in the virtual address space of any user process, so这就是使 kernel 能够更改映射但其他进程无法更改的原因)

It is impossible for a process to know where in actual memory a virtual address points to.进程不可能知道虚拟地址在实际 memory 中指向的位置。 That is possible for some administrator processes through a device (and a special device is needed for this) /dev/mem .某些管理员进程可以通过设备(为此需要特殊设备) /dev/mem来实现这一点。 But if you try to look in there without the actual mapping you'll get a mess of pages some belonging to a process, some to another, with no apparent structure, or you cannot also know what does it mean the contents of those pages.但是,如果您尝试在没有实际映射的情况下查看那里,您会得到一堆页面,一些属于一个进程,一些属于另一个,没有明显的结构,或者您也不知道这些页面的内容是什么意思。

There's another device /dev/kmem that maps to the kernel virtual address space, and allows a process (eg a debugger) to get access to the mapping table there.... but this is a very dangerous bend, as you can easily crash your system by tweaking there.还有另一个设备/dev/kmem映射到 kernel 虚拟地址空间,并允许进程(例如调试器)访问那里的映射表......但这是一个非常危险的弯道,因为你很容易崩溃通过在那里调整你的系统。 Think that you cannot normally stop the kernel (this stops the system) or if you can do, you will stop essential parts of the system that should be running.认为您不能正常停止 kernel(这会停止系统),或者如果可以,您将停止应该运行的系统的重要部分。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM