尝试理解 Linux 环境中 C++ 的内存映射

尝试理解 Linux 环境中 C++ 的内存映射

我的任务是探索内存映射,看看我们是否可以利用它。

我正在尝试理解这个概念以及如何编写代码。我一直在尝试以下视频和博客文章中的代码,但我也会在此处重复我的步骤,以便轻松重新创建。我正在 Linux 系统上使用 C++(gcc 版本 11.2.0)

https://www.youtube.com/watch?v=m7E9piHcfr4

https://bertvandenbroucke.netlify.app/2019/12/08/memory-mapping-files/

这是我的代码 write_mmap.cpp

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

int main() {
  // reading
  int file_read = open("test.dat", O_RDONLY, 0);
  // writing
  int file_write = open("test.dat", O_CREAT | O_RDWR,
                      S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
  posix_fallocate(file_write, 0, 4096);

  char *buffer = reinterpret_cast<char*> (mmap(NULL, 4096, PROT_WRITE, MAP_SHARED, file_write, 0));

  for (int i = 0; i < 4096; i++) {
    // Change each upper-case A to lowercase in the file
    if (buffer[i] == 'A') {
      buffer[i] = 'a';
    }
  }
}

看起来test.dat像这样

Name,marker1,marker2,marker3,marker4
barc1,AA,AB,BB,--
barc2,AB,AA,BB,--

运行命令后

g++ write_mmap.cpp
./a.out

我看到它test.dat已更改为

Name,marker1,marker2,marker3,marker4
barc1,aa,aB,BB,--
barc2,aB,aa,BB,--

这就是我想要的。我只是不确定这里的很多事情首先,是test.dat内存映射文件吗?当我ls在目录中时,我在文件系统中看到它,但它在虚拟内存中的某个地方吗?如果是这样,有什么方法可以找出它在虚拟内存中的存储位置吗?

为什么我写的时候它本来是一个文本文件,但在脚本完成后它变成了一个二进制文件 - 当我用打开文件时nano,我看到

Name,marker1,marker2,marker3,marker4
barc1,aa,aB,BB,--
barc2,aB,aa,BB,--
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^

我还想澄清一些有关内存映射的事情

  1. 这个文件现在在 RAM 中,对吧?任何程序(无论使用何种语言)都能够从内存的这些部分读取吗?

  2. 有什么方法可以在此文件中设置“书签”。那么如果我希望计算机从某个部分开始读取文件,我可以告诉它从地址 A 读取到地址 B 吗?

答案1

这个文件现在在 RAM 中,对吗?

不,该文件不在 RAM 中。文件的一部分位于 RAM 中。第二个参数请求mmap用于映射文件的内存段的大小。的最后一个参数mmap是文件内的位置。因此,在您的示例中,您将从文件开头开始映射 4Kb。如果原始文件有 4097 字节 - 最后一个字节将不会被映射。您可以使用mremap移动到文件中的不同块。

如果您想知道内存在哪里 - 只需查看 的返回即可mmap。您的变量buffer正是指向文件映射到的进程内存中某个位置的指针。

任何程序(无论使用何种语言)都能够从内存的这些部分读取吗?

是的。但它有点复杂。

首先,如果您执行 ammap然后fork- 子级将继承指针并可以访问完全相同的内存。

其次,如果另一个进程(无论使用哪种语言)决定映射相同的文件和文件中的相同块 - 它将有权访问相同的内存,但它将被映射到该进程的地址空间。换句话说 - 两个进程仍然可以争夺文件内容,但指向相同字节的指针在数字上会有所不同。

不要忘记,您可以混合使用大小和偏移量 - 第一个应用程序从偏移量 0 映射 4Kb 文件。另一个应用程序从偏移量 1024 映射同一文件的 1Kb。因此只有一个 1Kb 块会受到竞争。这种混合可能非常复杂......为了解决这个问题(或者使内存管理更容易),我们有“页面”。这是因为偏移量必须是页面大小的倍数的要求。

有什么方法可以在此文件中设置“书签”。那么如果我希望计算机从某个部分开始读取文件,我可以告诉它从地址 A 读取到地址 B 吗?

不,没有书签。但第 6 个和第 2 个参数可以mmap有效地做到这一点 - 地址A是最后一个参数和地址是最后一个+第二个参数mmap

顺便说一句,您不需要打开文件进行读取。在这种情况下这是多余的。现在,您有两个文件描述符指向同一个文件,但您忽略了只读文件描述符。不是犯罪,而是潜在的内存泄漏。

^@当您打开文件时看到的一堆nano内容是posix_fallocate.此函数可确保文件的大小符合mmap.从技术上讲,posix_fallocate如果您确定该文件足够大以填充 所请求的段,则不需要调用mmap。但如果您的文件太小 -posix_fallocate请将其放大。所以你现在有一个 4Kb 文件,开头填充了旧数据,尾部填充了零(^@- 中 0 字节的表示nano

相关内容