如何判断文件是否是内存映射的?


8

我对内存映射文件感到困惑,所以我有两个问题,如果您能帮助我,我将非常高兴。

  1. 假设我浏览到文件系统中的目录,并且该目录中有一个文件。该文件是否可能指向主内存中的区域,而不是指向磁盘中的区域?
  2. 如果可能的话,这就是我们所谓的“内存映射文件”吗?
  3. 在文件系统中移动此类文件(即,将mv此类文件从目录移动到另一个目录)的含义是什么?我的理解是,由于文件是内存映射的,因此与文件交互的进程始终写入主内存的预定义区域,并且当我们打开该文件(例如使用vim)时,我们将读取主内存的该区域。内存(因此,不涉及磁盘)。因此,无论我们将文件移到何处,它都将始终正确运行,对吗?如果是,在文件系统中移动文件是否有意义?
  4. 是否有一条命令可以判断文件是否已映射内存?
  5. 最后,如果我使用打开一个内存映射文件vim,请对其进行一些更改,然后保存并关闭vim,将会发生什么?我所做的更改是否只会写入主存储器?如果是这种情况,使用此文件的其他进程会看到我刚刚所做的更改吗?以我的经验,当我使用进行文件更改时,其他进程没有看到我对该文件所做的更改vim。这是什么原因呢?

12
这使我想起有人问如何确定文件是否为硬链接。
德米特里·格里戈列耶夫

3
@DmitryGrigoryev实际上,这很有趣,但是每个人都可以学到:)
cat

Answers:


24

内存映射文件的工作方式相反。内存映射不是文件的属性,而是访问文件的一种方式:进程可以将文件的内容(或其子集)映射到其地址空间。这使得读取和写入文件更加容易。这样做仅涉及在内存中进行读取和写入。磁盘上的文件本身与任何其他文件相同。

要进行设置,进程使用该mmap功能。这也可以用于其他目的,例如在进程之间共享内存。


14
@Utku这与内存映射文件无关。
佐藤桂

12
如果您没有关闭MySQL服务器,那是正常现象:该服务器在文件上打开了文件描述符,即使使用,该描述符也仍然有效mv
史蒂芬·基特

11
文件描述符最终指向文件系统中的索引节点。那才是文件真正存在的地方。目录条目也指向这些inode,并且mv仅更改目录条目,而不更改inode(当它在同一文件系统上移动文件时)。
史蒂芬·基特

1
您的描述是一个有用的简化,但是只是出于准确性:内存映射从技术上讲与文件描述符不同,但是它们的工作方式相同(通过引用inode,而不是文件名)。open(),mmap(),close()不会留下任何FD,只是一个映射,它将显示为lsof。直到进程调用munmap()或退出(或使用mmap(MAP_FIXED)替换为另一映射),它才会消失
Peter Cordes

3
@Utku您实际上没有移动文件。您刚刚创建了一个引用相同文件的新目录条目,然后删除了旧文件。命名更改对已经打开文件的进程没有影响。
大卫·史瓦兹

11

内存映射文件不(不一定)由内存支持。它可以完美地存在于磁盘上。实际上,文件所在的位置不是文件本身的属性,而是文件所在的文件系统的属性。

将文件映射到内存中是进程可以执行的将文件的一部分加载到内存中的操作。结果看起来像一个常规的内存区域,除了当进程从该区域读取或写入该区域时,实际上是从文件读取和写入该区域。如果打开文件,将其映射到内存,写入并保存,则修改将在文件,磁盘上进行(当然,如果它位于磁盘上)。

这可以用来例如,当你知道你有很多的访问做一个文件,它不会是连续的,是因为它可以更容易,更有效的做读取和写入存储器,而不是问题readwrite,和llseek系统调用。此方法的唯一问题是,如果需要同时从多个进程读取或写入文件,则无法真正使用它。结果将是不可预测的。

我知道没有命令可以告诉您当前是否已映射文件。但是,您可以在中检查进程的映射/proc/<pid>/maps(如果您的系统中有)。

要回答第二个问题,即使打开文件,即使在文件系统中移动文件,打开文件的进程仍可以使用它。发生的事情是文件不依赖于文件系统中的条目。只要打开了文件,就拥有一个“句柄”,即文件描述符,即使文件系统中其路径发生了变化,也可以使用它来读取和写入文件。仅当文件在文件系统中没有条目并且没有进程在其上保留文件描述符时,文件才会消失。


因此,当我们移动文件时,文件描述符的值不会更改。有一个路径到文件的描述符映射,只有该映射的路径部分会更改。它是否正确?
Utku

1
从某种意义上说是的,但是我不确定您是否了解您,所以让我改一下。基本上,“文件”是三件事。目录条目是文件系统中的路径。索引节点是文件的内容。文件描述符表示一个打开的文件。目录条目和文件描述符都包含一个指向其后备索引节点的指针。打开文件时,您传递目录条目,内核将为您返回文件描述符。因此,即使原始目录条目更改了,文件描述符仍然指向相同的inode,并且您可以访问文件。
lgeorget

1
不过,您可以在中检查流程的映射/proc/<pid>/maps-只要上述过程存在于必须以a /proc开头的系统上。OpenBSD没有,而FreeBSD正在逐步淘汰它。另外,FreeBSD具有/proc/<pid>/map而不是/proc/<pid>/maps
佐藤桂

@SatoKatsura谢谢您的精确。我手头只有一台Linux机器,所以我想我要说说我的情况,然后让人们说说他们的情况。
lgeorget

既然您问了:您假设OP确实了解他的要求,并详细说明了什么是内存映射文件。我不认为你为他服务。海事组织(IMO),您在上面的第一条评论与操作人员实际要问的内容和您的答案息息相关。FWIW。
佐藤桂

9

Q4:是否有一个命令,可以告诉一个文件是否是内存映射?

lsof命令将显示系统当前正在使用的所有文件。如果文件是内存映射的,则“ FD”列将包含“ mem”。因此,您可以将此命令的输出grep转换为您感兴趣的文件名。


3
或者使用lsof -ad mem /path/to/file
斯特凡Chazelas

5
或者更确切地说lsof -ad mem,txt /path/to/file,正在执行的文件在进程地址空间中也有一部分被映射,但txtlsof输出中显示为。
斯特凡Chazelas

7

您似乎将内存映射与驻留在内存的文件系统中的文件以及其他概念(例如,进程在移动时如何保持对文件的访问权)相混淆。

我将一个问题一个问题地问我是否可以解决问题。

  1. 假设我浏览到文件系统中的目录,并且该目录中有一个文件。该文件是否可能指向主内存中的区域,而不是指向磁盘中的区域?

如果它位于驻留内存的文件系统上,则它确实指向主内存,例如通常安装在/ proc上的procfs或/ sys上的sysfs或有时在/ tmp上的tmpfs。

  1. 如果可能的话,这就是我们所谓的“内存映射文件”吗?

不会。就像stephen-kitt所说的那样,“内存映射”是一种通过在主内存上“映射”文件并在其中使用文件来访问文件的方法,而不是通过read()和写()。

  1. 在文件系统中移动此类文件(即,将此类文件从目录移动到另一个目录)的含义是什么?我了解的是,由于文件是内存映射的,因此与文件交互的进程始终写入主内存的预定义区域,并且当我们打开该文件(例如,使用vim)时,我们会读取该区域。主内存(因此,不涉及磁盘)。因此,无论我们将文件移到何处,它都将始终正确运行,对吗?如果是,在文件系统中移动文件是否有意义?

如果在同一个文件系统中移动它,实际上就是在一个引用(从一个目录到另一个目录的索引节点)中移动。如果已经打开了该文件的程序,则它们仍将访问同一文件,因为它们已经通过文件描述符准备好了索引节点。这就是您在注释中提到的table_name.idb文件所发生的情况。

  1. 是否有一条命令可以判断文件是否已映射内存?

Wossname已针对内存映射文件回答了此问题。lsof会告诉您哪些进程具有文件内存映射。

要知道文件是否在驻留内存的文件系统中,可以使用dfmount列出文件系统及其挂载点。您只需要通过查找即可知道哪种类型的文件系统驻留在内存中(例如,在Wikipedia中)。

  1. 最后,如果我用vim打开一个内存映射文件,对其进行一些更改并保存并关闭vim,会发生什么?我所做的更改是否只会写入主存储器?如果是这样,使用此文件的其他进程会看到我刚刚所做的更改吗?以我的经验,当我使用vim对文件进行一些更改时,其他进程没有看到我对该文件所做的更改。这是什么原因呢?

就我个人而言,我还没有mmap在C程序中使用过该函数,但是据我从略读man mmap和了解到info mmap,在保持内存中的表示同步方面并没有任何魔术。调用mmap的基本形式是将文件内容复制到内存,msync并用于将其从内存写回到磁盘。如果磁盘文件发生更改,则没有任何位置可以检测到该文件并自动在映射该文件的所有进程中修改内存中的表示形式。

编辑:事实证明,在某些情况下,mmap()实际上确实尝试使内存中的表示形式保持同步。如果仅从读取映射,则即使其他进程写入文件,也将保持同步。如果将其写入(通过分配给内存区域),则发生的情况取决于向mmap()提供了哪些表面上必需的MAP_SHARED或MAP_PRIVATE标志。如果提供了MAP_PRIVATE,则映射从磁盘上的表示派生,并停止同步,直到您使用msync()为止。如果提供了MAP_SHARED,则更新将对其他已映射文件的进程以及磁盘上的表示形式可见(尽管这不一定是立即的)。

我只是在一个现有文件上打开vim e,然后在另一个终端:winotifywait -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创建一个新文件,并删除旧文件。为什么这样做而不是修改文件超出了此问题的范围,但是重点是这是一个新文件,因此具有新的inode。

现在,使用此文件的其他进程对您意味着什么?如果您是指在执行此操作的同时打开了文件的进程,则不会,他们将看不到更改。这是因为,尽管他们打开了具有相同路径的文件,但它们不是同一文件。如果您指的是执行此操作后可能会打开文件的进程,那么可以,他们将看到更改。他们将打开您创建的新文件。

需要注意的重要一点是,尽管程序似乎在用户界面上打开了文件,但这并不一定意味着它们在此过程中保持文件打开。如上所示,Vim就是一个例子。


3
如果磁盘上的文件发生了变化,则没有任何东西可以检测到它并自动在映射它的所有进程中修改内存中的表示形式。 ”将会改变操作系统页面映射背后的磁盘上文件系统的原因系统?您是否正在想象对块设备或通过iSCSI共享的块设备的某种原始访问?
大卫·史瓦兹

@ david-schwartz不,我在想象两个具有文件open()的进程。进程1使用mmap()将文件内容复制/映射到内存。然后,进程2使用write()(可能还有fsync())来更改磁盘上的内容。这时,文件内容进程1在内存中没有反映进程2所做的更改,对吗?
JoL

不,当然不。该write功能的目的是更改文件数据。这可能意味着更改磁盘内容,也可能并不意味着更改磁盘内容,但是无论涉及什么内容,文件系统都有责任正确处理它。在这种情况下,将涉及修改内存的映射页面并将其标记为脏。
大卫·史瓦兹

@ david-schwartz我尝试了mmap(),您是对的。在我之前的评论中介绍的场景中,除非进程1 事先已写入映射中的内存,否则进程1 在内存中(在映射中)的内容实际上确实反映了更改。即使更改过程1所做的更改与更改过程2所做的更改位于不同的位置,也是如此。我更新了答案,以找出不正确的地方并添加发现的内容。
JoL

1
@ david-schwartz对不起,我并不是要暗示mmap的行为与文档规定的有所不同,但是是的,我想我使答案太混乱了。我认为它仍在范围内,但是“使用该文件的其他进程是否会看到我刚刚所做的更改?”这个问题似乎太笼统了。“太多取决于”。因为OP的需求似乎纯粹是自言自语,所以我试图给出一个准确的答案,并尽我所能覆盖所有范围,但是我可能已经过头了。虽然,但我还是很高兴,因为我也学到了很多。
JoL
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.