我对内存映射文件感到困惑,所以我有几个问题,如果您能帮助我,我将非常高兴。
- 假设我浏览到文件系统中的一个目录,并且该目录中有一个文件。有没有可能这个文件指向主存中的某个区域,而不是指向磁盘中的某个区域?
- 如果这是可能的,这就是我们所说的“内存映射文件”吗?
- 在文件系统中移动此类文件(即,将此类文件从一个目录移动到另一个目录)有何意义
mv
?我的理解是,由于文件是内存映射的,与文件交互的进程总是写入主内存的预定义区域,当我们打开该文件(例如使用vim
)时,我们读取主内存的该区域内存(因此,不涉及磁盘)。因此,无论我们将文件移动到哪里,它总是能正常工作,对吗?如果是,在文件系统中移动文件有什么意义吗? - 是否有一个命令可以判断文件是否是内存映射的?
- 最后,如果我使用 打开一个内存映射文件
vim
,对其进行一些更改并保存并关闭vim
,会发生什么?我的更改会简单地写入主内存吗?如果是这种情况,使用该文件的其他进程会看到我刚刚所做的更改吗?根据我的经验,当我对文件进行一些更改时,其他进程没有看到我对文件所做的更改vim
。这是什么原因呢?
答案1
内存映射文件的工作方式正好相反。内存映射不是文件的属性,而是访问文件的一种方式:进程可以将文件的内容(或其子集)映射到其地址空间。这使得读取和写入文件变得更加容易;这样做只涉及在内存中进行读写。磁盘上的文件本身与任何其他文件相同。
为了进行设置,进程使用mmap
功能。这也可以用于其他目的,例如在进程之间共享内存。
答案2
内存映射文件(不一定)不受内存支持。它可以完美地存在于磁盘上。实际上,文件所在的位置不是文件本身的属性,而是它所在的文件系统的属性。
在内存中映射文件是进程可以执行的将文件的一部分加载到内存中的操作。结果看起来像一个常规的内存区域,只不过当进程读取或写入该区域时,它实际上是读取和写入文件。如果您打开一个文件,将其映射到内存,写入并保存它,则修改将在磁盘上的文件上完成(当然,如果它位于磁盘上)。
例如,当您知道要对一个文件进行大量访问时,可以使用此方法,这些访问不会是连续的,因为在内存中进行读取和写入比发出read
, write
,和llseek
系统调用。此方法的唯一问题是,如果需要多个进程同时读取或写入文件,则无法真正使用它。结果将是不可预测的。
我不知道任何命令可以告诉您文件当前是否已映射。不过,您可以检查进程的映射/proc/<pid>/maps
(如果您的系统有)。
要回答你的第二个问题,当你打开一个文件时,即使你将它移动到文件系统中,打开它的进程仍然可以使用它。发生的情况是文件不依赖于文件系统中的条目。只要你打开了一个文件,你就有一个“句柄”,一个文件描述符,它可以让你读取和写入它,即使它在文件系统中的路径发生了变化。仅当文件在文件系统中没有条目并且没有进程在其上保存文件描述符时,该文件才会消失。
答案3
Q4:是否有命令可以判断文件是否是内存映射的?
该lsof
命令将显示系统当前使用的所有文件。如果文件是内存映射的,“FD”列将包含“mem”。因此,您可以 grep 该命令的输出来查找您感兴趣的文件名。
答案4
您似乎将内存映射与内存驻留文件系统中的文件以及其他概念(例如进程如何在文件移动时保持对文件的访问权限)混淆。
我会一个问题一个问题地问,看看是否能把事情弄清楚。
- 假设我浏览到文件系统中的一个目录,并且该目录中有一个文件。有没有可能这个文件指向主存中的某个区域,而不是指向磁盘中的某个区域?
如果它位于内存驻留文件系统上,则它确实指向主内存,例如通常安装在 /proc 上的 procfs,或者安装在 /sys 上的 sysfs,或者有时安装在 /tmp 上的 tmpfs。
- 如果这是可能的,这就是我们所说的“内存映射文件”吗?
不。就像 Stephen-kitt 所说,“内存映射”是指一种通过将文件“映射”到主内存上并在那里使用它来访问文件的方法,而不是通过 read() 等函数一次读取和写入块写()。
- 在文件系统中移动此类文件(即,将此类文件从一个目录移动到另一个目录)有何意义?我的理解是,由于文件是内存映射的,与文件交互的进程总是写入主内存的预定义区域,当我们打开该文件(例如使用 vim)时,我们会读取该区域主存储器(因此,不涉及磁盘)。因此,无论我们将文件移动到哪里,它总是能正常工作,对吗?如果是,在文件系统中移动文件有什么意义吗?
如果您在同一文件系统中移动它,那么您实际上只是在引用(即索引节点)从一个目录移动到另一个目录。如果有程序已经打开了该文件,它们仍然会访问同一个文件,因为它们已经通过文件描述符拥有了 inode。这就是您在评论中提到的 table_name.idb 文件发生的情况。
- 是否有一个命令可以判断文件是否是内存映射的?
Wossname 已经针对内存映射文件回答了这个问题。lsof
会告诉您哪些进程具有文件内存映射。
要了解文件是否位于内存驻留文件系统中,可以使用df
或
mount
列出文件系统及其挂载点。您只需要通过查找(例如在维基百科中)来了解哪些类型的文件系统驻留在内存中。
- 最后,如果我用 vim 打开一个内存映射文件,对其进行一些更改,然后保存并关闭 vim,会发生什么?我的更改会简单地写入主内存吗?如果是这种情况,使用该文件的其他进程会看到我刚刚所做的更改吗?根据我的经验,当我用 vim 对文件进行一些更改时,其他进程没有看到我对文件所做的更改。这是什么原因呢?
就我个人而言,我没有mmap
在 C 程序中使用过该函数,但据我从略读man mmap
和了解info mmap
,保持内存中表示同步并不涉及任何魔法。在其基本形式中,调用 mmap 将文件内容复制到内存,并msync
用于将其从内存写回到磁盘。如果磁盘上的文件发生更改,则没有任何方法可以检测到该情况并自动修改映射它的所有进程中的内存中表示。
编辑:事实证明 mmap() 实际上确实尝试在某些条件下保持内存中表示同步。如果仅读取映射,即使其他进程写入该文件,它也会保持同步。如果它被写入(通过分配给内存区域),会发生什么取决于向 mmap() 提供明显强制的 MAP_SHARED 或 MAP_PRIVATE 标志中的哪一个。如果提供了 MAP_PRIVATE,映射将从磁盘上的表示中分叉出来并停止同步,直到您使用 msync()。如果提供了 MAP_SHARED,则更新对于映射文件的其他进程以及(尽管这不一定是立即的)磁盘上的表示可见。
我刚刚在现有文件上打开 vim ,并在另一个终端中e
运行命令。除了一些奇怪的部分之外,这是我从中获得的重要部分。:w
inotifywait -m .
inotifywait
./ MOVED_FROM e
./ MOVED_TO e~
./ CREATE e
./ OPEN e
./ MODIFY e
./ CLOSE_WRITE,CLOSE e
./ ATTRIB e
./ ATTRIB e
./ DELETE e~
Vim 创建一个新文件,并删除旧文件。为什么它这样做而不是修改文件超出了这个问题的范围,但重点是这是一个新文件,因此有一个新的索引节点。
现在,其他进程使用该文件是什么意思?如果您的意思是在执行此操作时打开了文件的进程,那么它们不会看到更改。这是因为,虽然它们打开了具有相同路径的文件,但它们不是同一个文件。如果您的意思是在执行此操作后可能打开文件的进程,那么是的,他们会看到更改。他们将打开您创建的新文件。
值得注意的是,虽然程序似乎在用户界面上打开了一个文件,但这并不一定意味着它们在进程中保持文件打开。 Vim 就是一个例子,如上所示。