我的任务是探索内存映射,看看我们是否可以利用它。
我正在尝试理解这个概念以及如何编写代码。我一直在尝试以下视频和博客文章中的代码,但我也会在此处重复我的步骤,以便轻松重新创建。我正在 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,--
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^
我还想澄清一些有关内存映射的事情
这个文件现在在 RAM 中,对吧?任何程序(无论使用何种语言)都能够从内存的这些部分读取吗?
有什么方法可以在此文件中设置“书签”。那么如果我希望计算机从某个部分开始读取文件,我可以告诉它从地址 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
)