什么是内存映射页和匿名页?

问题描述 投票:0回答:3

我无法理解Linux中的内存映射页面和匿名页面。有人可以用例子解释一下吗?与之相关的内核数据结构有哪些?

linux memory-management linux-kernel memory-mapped-files memory-mapping
3个回答
95
投票

正确的术语是内存映射文件和匿名映射。当提到内存映射时,通常指的是 mmap(2)。使用 mmap 有 2 类。一类是共享映射与私有映射。另一类是文件与匿名映射。混合在一起你会得到以下 4 种组合:

  1. 私有文件映射
  2. 共享文件映射
  3. 私人匿名映射
  4. 共享匿名映射

文件映射指定磁盘上的一个文件,该文件将有 N 个字节映射到内存中。函数 mmap(2) 将要映射到内存的文件的文件描述符作为其第四个参数。第 5 个参数是要读入的字节数,作为偏移量。使用mmap创建内存映射文件的典型过程是这样的

  1. open(2) 文件以获取文件描述符。
  2. fstat(2) 从文件描述符数据结构中获取文件大小。
  3. mmap(2) 使用从 open(2) 返回的文件描述符的文件。
  4. close(2) 文件描述符。
  5. 对内存映射文件执行任何操作。

当文件映射为 PRIVATE 时,所做的更改不会提交到基础文件。它是文件的私有内存副本。当文件映射为 SHARED 时,所做的更改将由内核自动提交到底层文件。映射为共享的文件可用于所谓的内存映射 I/O 和 IPC。如果您需要文件的持久性,您可以使用 IPC 的内存映射文件而不是共享内存段

如果您使用 strace(1) 观察进程初始化,您会注意到文件的不同部分使用 mmap(2) 作为私有文件映射进行映射。系统库也是如此。

strace(1) 的输出示例,其中使用 mmap(2) 将库映射到进程。

open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=42238, ...}) = 0
mmap(NULL, 42238, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ff7ca71e000
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY)      = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p\356\341n8\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1926760, ...}) = 0
mmap(0x386ee00000, 3750152, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x386ee00000

匿名映射不受文件支持。具体来说,当 MAP_ANONYMOUS 标志用作 mmap(2) 的第三个参数时,甚至不使用 mmap(2) 的第四个(文件描述符)和第五个(偏移量)参数。使用 MAP_ANONYMOUS 标志的替代方法是使用 /dev/zero 作为文件。

对我来说,“匿名”这个词是一个糟糕的选择,因为它听起来好像文件是匿名映射的。相反,它是匿名文件,即。没有指定文件。

匿名私有映射最常见的用途是进程的堆和栈段。您可以使用共享匿名映射,以便应用程序可以共享内存区域,但我不知道为什么您不使用 SYSV 或 POSIX 共享内存。

由于使用匿名映射映射的内存保证是零填充的,因此对于某些期望/需要零填充内存区域的应用程序来说,以这种方式使用 mmap(2) 而不是 malloc(2) + memset( 2)组合。


11
投票

据我了解,匿名页面之所以如此命名,是因为它们没有命名的文件系统源,而映射页面是具体文件的映射。例如,您可以在任何用户空间进程中使用简单的 malloc 操作来获取匿名页面...

关于内核结构: 显然它是 struct page,但是对于匿名页面,您将在页面->映射中看到 struct anon_vma,对于映射页面 - struct address_space,它与具体的 inode 连接。


4
投票

我不确定内存映射页面是什么意思?所以我不会谈论这个。

关于匿名页面,通常在内核进行页框回收时引用。匿名页面的实例包括进程的堆栈、堆、共享内存和任何修改的共享库。在Linux中,所有动态共享库都通过系统调用映射到进程的虚拟内存地址空间,如下所示:

firo@linux-6qg8:~> strace -e mmap,openat ls 2>&1 |grep -A1 libc.so
openat(AT_FDCWD, "/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
mmap(NULL, 3906144, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0)

对属于 MAP_PRIVATEed 文件/库的页面进行任何写入都会触发从文件后端页面到匿名页面的更改。

根据定义,匿名页面,也称为匿名内存,只是一种在内核进行页框回收时没有后端设备可以交换到的页面。这就是 Linux 支持交换区的原因。

有两种与匿名页面相关的内核数据结构。

  1. 为了回收匿名页面,内核必须知道所有正在使用匿名页面来更改其 PTE(页表条目)的进程。我们称之为反向映射或 rmap。

    struct address_space 被共享内存用来维护反向映射。

    struct anon_vma 被其余匿名页面用来维护反向映射。

  2. 内核使用LRU算法来回收页框。 对于内核 5.0+,请检查 struct pglist_data 中的 struct lruvec

© www.soinside.com 2019 - 2024. All rights reserved.