Linux Kernel 学习笔记二(内存管理)(2008-08-05 12:27:40)
Memory Management (内存管理)
——Linux
Kernel 学习笔记二
内存管理子系统是操作系统的重要部分。从计算机发展早期开始,就存在对于大于系统中物理能力的内存需要。为了克服这种限制,开发了许多种策略,其中最成功的就是虚拟内存。虚拟内存通过在竞争进程之间共享内存的方式使系统显得拥有比实际更多的内存。
虚拟内存不仅仅让你的计算机内存显得更多,内存管理子系统还提供:
Large Address Spaces
(巨大的地址空间)操作系统使系统显得拥有比实际更大量的内存。虚拟内存可以比系统中的物理内存大许多倍。
Protection
(保护)系统中的每一个进程都有自己的虚拟地址空间。这些虚拟的地址空间是相互完全分离的,所以运行一个应用程序的进程不会影响另外的进程。另外,硬件的虚拟内存机制允许对内存区写保护。这可以防止代码和数据被恶意的程序覆盖。
Memory Mapping
(内存映射)内存映射用来将映像和数据映射到进程的地址空间。用内存映射,文件的内容被直接连结到进程的虚拟地址空间。
Fair Physics Memory
Allocation
(公平分配物理内存)内存管理子系统允许系统中每一个运行中的进程公平地共享系统的物理内存
Shared
Virtual Memory
(共享虚拟内存)虽然虚拟内存允许进程拥有分离(虚拟)的地址空间,有时你也需要进程之间共享内存。例如,系统中可能有多个进程运行命令解释程序
bash 。虽然可以在每一个进程的虚拟地址空间都拥有一份 bash 的拷贝,更好的是在物理内存中只拥有一份拷贝,所有运行 bash
的进程共享代码。动态连接库是多个进程共享执行代码的另一个常见例子。共享内存也可以用于进程间通讯
(IPC) 机制,两个或多个进程可以通过共同拥有的内存交换信息。 Linux 系统支持系统 V 的共享内存 IPC
机制。
1 An Abstract Model of Virtual
Memory (虚拟内存的抽象模型)
在考虑 Linux
支持虚拟内存的方法之前,最好先考虑一个抽象的模型,以免被太多的细节搞乱。
为了让这种转换更简单,将虚拟内存和物理内存分为适当大小的块,叫做页( page
)。页的大小一样。Linux 在 Alpha AXP 系统上使用 8K 字节的页,而在 Intel x86 系统上使用 4K
字节的页。每一页都赋予一个唯一编号: page frame number(PFN 页编号 )
。在这种分页模型下,虚拟地址由两部分组成:虚拟页号和页内偏移量。假如页大小是 4K
,则虚拟地址的位 11 到 0
包括页内偏移量,位 12
和以上的位是页编号。每一次处理器遇到虚拟地址,它必须提取出偏移和虚拟页编号。处理器必须将虚拟页编号转换到物理的页,并访问物理页的正确偏移处。理论上页表每一个条目包括以下信息:
有效标志表示页表本条目是否有效
本页表条目描述的物理页编号
访问控制信息描述本页如何使用:是否可以写?是否包括执行代码?
页表通过虚拟页标号作为偏移来访问。虚拟页编号 5 是表中的第 6 个元素( 0 是第一个元素)
要将虚拟地址转换到物理地址,处理器首先找出虚拟地址的页编号和页内偏移量。使用 2
的幂次的页尺寸,可以用掩码或移位简单地处理。再一次看图 3.1 ,假设页大小是 0x2000 (十进制 8192 ),进程 Y
的虚拟地址空间的地址是 0x2194 ,处理器将会把地址转换为虚拟页编号 1 内的偏移量 0x194 。

处理器使用虚拟页编号作为索引在进程的页表中找到它的页表的条目。如果该条目有效,处理器从该条目取出物理的页编号。如果本条目无效,就是进程访问了它的虚拟内存中不存在的区域。在这种情况下,处理器无法解释地址,必须将控制权传递给操作系统来处理。
处理器具体如何通知操作系统进程在访问无法转换的无效的虚拟地址,这个方式是和处理器相关的。处理器将这种信息(
page fault )进行传递,操作系统得到通知,虚拟地址出错,以及出错的原因。
假设这是一个有效的页表条目,处理器取出物理页号并乘以页大小,得到了物理内存中本页的基础地址。最后,处理器加上它需要的指令或数据的偏移量。
再用上述例子,进程 Y 的虚拟页编号 1 映射到了物理页编号 4 (起始于 0x8000 , 4x 0x2000 ),加上偏移
0x194 ,得到了最终的物理地址 0x8194 。
通过这种方式将虚拟地址映射到物理地址,虚拟内存可以用任意顺序映射到系统的物理内存中。例如,图
3.1 中,虚拟内存 X 的虚拟页编号映射到了物理页编号 1
而虚拟页编号 7 虽然在虚拟内存中比虚拟页 0
要高,却映射到了物理页编号 0
。这也演示了虚拟内存的一个有趣的副产品:虚拟内存页不必按指定顺序映射到物理内存中。
1 Shared Vitual Memory (共享虚拟内存)
虚拟内存使多个进程可以方便地共享内存。所有的内存访问都是通过页表,每一个进程都有自己的页表。对于两个共享一个物理内存页的进程,这个物理页编号必须出现在两个进程的页表中。
图
3.1 显示了两个共享物理页号 4 的进程。对于进程 X 虚拟页号是 4 ,而对于进程 Y 虚拟页号是 6
。这也表明了共享页的一个有趣的地方:共享的物理页不必存在共享它的进程的虚拟内存空间的同一个地方。
2 Linux Page Tables ( Linux
页表)
Linux 假定了三级页表。访问的每一个页表包括了下一级页表的页编号。图 3.3
显示了一个虚拟地址如何分为一系列字段:每一个字段提供了在一个页表中的偏移量。为了将虚拟地址转换为物理地址,处理器必须取得每一级字段的内容,转换为包括该页表的物理页内的偏移,然后读取下一级页表的页编号。重复三次直到包括虚拟地址的物理地址的页编号找到为止。然后用虚拟地址中的最后一个字段:字节偏移量,在页内查找数据。
Linux
运行的每一个平台都必须提供转换宏,让核心处理特定进程的页表。这样,核心不需要知道页表条目的具体结构或者如何组织。通过这种方式,
Linux 成功地使用了相同的页表处理程序用于 Alpha 和 Intel x86
处理器,其中 Alpha 使用三级页表,而 Intel
使用二级页表。
参见 include/asm/pgtable.h
3 Page Allocation and Deallocation (
页的分配和回收 )
系统中对于物理页有大量的需求。例如,当程序映像加载到内存中的时候,操作系统需要分配页。当程序结束执行并卸载时需要释放这些页。另外为了存放核心相关的数据结构比如页表自身,也需要物理页。这种用于分配和回收页的机制和数据结构对于维护虚拟内存子系统的效率也许是最重要的。
系统中的所有的物理页都使用 mem_map 数据结构来描述。这是一个 mem_map_t
结构的链表,在启动时进行初始化。每一个 mem_map_t (容易混淆的是这个结构也被称为 page
结构)结构描述系统中的一个物理页。
每一个 free_area 单元包括页块的信息。数组中的第一个单元描述了单页,下一个是 2 页大小的块,下一个是 4
页大小的块,以此类推,依次向上都是 2 的倍数。这个链表单元用作队列的开头,有指向 mem_map
数组中页的数据结构的指针。空闲的页块在这里排队。 Map
是一个跟踪这么大小的页的分配组的位图。如果页块中的第 N
块空闲,则位图中的第 N 位置位。
图3.4显示了
free_area 结构。单元0有一个空闲页(页编号0),单元2有2个4页的空闲块,第一个起始于页编号4,第二个起始于页编号56
。

4.1 Page Allocation (
页分配 )
参见 mm/page_alloc.c get_free_pages()
Linux
使用 Buddy
算法有效地分配和回收页块。页分配代码试图分配一个由一个或多个物理页组成的块。页分配使用 2
的幂数大小的块。这意味着可以分配 1 页大小, 2
页大小, 4 页大小的块,依此类推。只要系统有满足需要的足够的空闲页( nr_free_pages
> min_free_pages ),分配代码就会在
free_area 中查找满足需要大小的一个页块。 Free_area
中的每一个单元都有描述自身大小的页块的占用和空闲情况的位图。例如,数组中的第 2
个单元拥有描述 4 页大小的块的空闲和占用的分配图。
这个算法首先找它请求大小的内存页块。它跟踪 free_area 数据结构中的 list
单元队列中的空闲页的链表。如果请求大小的页块没有空闲,就找下一个尺寸的块( 2
倍于请求的大小)。继续这一过程一直到遍历了所有的 free_area
或者找到了空闲页块。如果找到的页块大于请求的页块,则该块将被分开成为合适大小的块。因为所有的块都是
2
的幂次的页数组成,所以这个分割的过程比较简单,你只需要将它平分就可以了。空闲的块则放到适当的队列,而分配的页块则返回给调用者。
例如在图 3.4 中,如果请求 2 页的数据块,第一个 4 页块(起始于页编号 4
)将会被分为两个 2 页块。起始于页号 4 的第一个 2 页块将会被返回给调用者,而第二个 2 页块(起始于页号 6 )将会排在
free_area 数组中的单元 1 中 2 页空闲块的队列中。
4.2 Page Deallocation
(页回收)
分配页块的过程中将大的页块分为小的页块,将会使内存更为零散。页回收的代码只要可能就把页联成大的页块。其实页块的大小很重要(
2 的幂数),因为这样才能很容易将页块组成大的页块。
只要一个页块回收,就检查它的相邻或一起的同样大小的页块是否空闲。如果是这样,就把它和新释放的页块一起组成以一个新的下一个大小的空闲页块。每一次两个内存页块组合成为更大的页块时,页回收代码都要试图将页块合并成为更大的块。这样,空闲的页块就会尽可能的大。
例如,在图
3.4 ,如果页号 1 释放,那么它会和已经空闲的页号 0 一起组合并放在 free_area 的单元 1 中空闲的 2
页块队列中。
6 Demand
Paging
只要执行映像映射到进程的虚拟内存中,它就可以开始运行。因为只有映像的最开始的部分是放在物理内存中,很快就会访问到还没有放在物理内存的虚拟空间区。当进程访问没有有效页表条目的虚拟地址的时候,处理器向
Linux 报告 page fault 。 Page fault 描述了发生 page fault
的虚拟地址和内存访问类型。
Linux
必须找到 page fault 发生的空间区所对应的
vm_area_struct 数据结构(用 Adelson-Velskii and Landis
AVL
树型结构连接在一起)。
并非所有的 vm_area_struct
数据结构都有一整套虚拟内存操作,而且那些有特殊的内存操作的也可能没有 nopang
操作。因为缺省情况下,对于 nopage 操作, Linux
会分配一个新的物理页并创建有效的页表条目。如果这一段虚拟内存有特殊的 nopage 操作, Linux
会调用这个特殊的代码。
通常的 Linux
nopage 操作用于对执行映像的内存映射,并使用 page cache
将请求的映像页加载到物理内存中。虽然在请求的页调入的物理内存中以后,进程的页表得到更新,但是也许需要必要的硬件动作来更新这些条目,特别是如果处理器使用了
TLB 。既然 page fault 得到了处理,就可以扔在一边,进程在引起虚拟内存访问错误的指令那里重新运行。


7 The Linux Page
Cache
Linux 的 page cache
的作用是加速对于磁盘文件的访问。内存映射文件每一次读入一页,这些页被存放在 page cache
中。图 3.6 显示了 page cache ,包括一个指向
mem_map_t 数据结构的指针向量: page_hash_table
。 Linux 中的每一个文件都用一个 VFS inode
的数据结构标示(在第 9 章描述),每一个 VFS I
节点都是唯一的并可以完全确定唯一的一个文件。页表的索引取自 VFS 的 I 节点号和文件中的偏移。
参见 linux/pagemap.h
当一页的数据从内存映射文件中读出,例如当 demand paging
时需要放到内存中的时候,此页通过 page cache 中读出。如果此页在缓存中,就返回一个指向
mem_map_t 数据结构的指针给 page fault
的处理代码。否则,此页必须从存放此文件的文件系统中加载到内存中。 Linux
分配物理内存并从磁盘文件中读出该页。如果可能, Linux
会启动对文件下一页的读。这种单页的超前读意味着如果进程从文件中顺序读数据的话,下一页数据将会在内存中等待。
当程序映像读取和执行的时候 page cache
不断增长。如果页不在需要,将从缓存中删除。比如不再被任何进程使用的映像。当 Linux
使用内存的时候,物理页可能不断减少,这时 Linux 可以减小 page cache 。
8 Swapping out and Discarding Pages
(交换出去和废弃页)
当物理内存缺乏的时候,
Linux 内存管理子系统必须试图释放物理页。这个任务落在核心交换进程上( kswapd
)。核心交换守护进程是一种特殊类型的进程,一个核心线程。核心线程是没有虚拟内存的进程,以核心态运行在物理地址空间。核心交换守护进程名字有一点不恰当,因为它不仅仅是将页交换到系统交换文件上。它的任务是保证系统有足够的空闲页,使内存管理系统有效地运行。
核心交换守护进程( kswapd )在启动时由核心的 init
进程启动,并等待核心的交换计时器到期。每一次计时器到期,交换进程检查系统中的空闲页数是否太少。它使用两个变量:
free_pages_high 和 free_pages_low
来决定是否释放一些页。只要系统中的空闲页数保持在 free_pages_high
之上,交换进程什么都不做。它重新睡眠直到它的计时器下一次到期。为了做这种检查,交换进程要考虑正在向交换文件中写的页数,用
nr_async_pages
来计数:每一次一页排到队列中等待写到交换文件中的时候增加,写完的时候减少。
Free_page_low 和 free_page_high
是系统启动时间设置的,和系统中的物理页数相关。如果系统中的空闲页数小于 free_pages_high
或者比 free_page_low
还低,核心交换进程会尝试三种方法来减少系统使用的物理页数: 参见 mm/vmscan.c 中的
kswapd()
减少 buffer cache 和 page cache 的大小
将系统 V 的共享内存页交换出去
交换和废弃页
如果系统中的空闲页数低于
free_pages_low ,核心交换进程将试图在下一次运行前释放 6 页。否则试图释放 3
页。以上的每一种方法都要被尝试直到释放了足够的页。核心交换进程记录了它上一次使用的释放物理页的方法。每一次运行时它都会首先尝试上一次成功的方法来释放页。
释放了足够的页之后,交换进程又一次睡眠,直到它的计时器又一次过期。如果核心交换进程释放页的原因是系统空闲页的数量少于
free_pages_low ,它只睡眠平时的一半时间。只要空闲页数大于 free_pages_low
,交换进程就恢复原来的时间间隔进行检查。
8.1 Reducing the size of the Page and
Buffer
Caches
page 和 buffer cache 中的页是释放到
free_area 向量中的好选择。 Page Cache
,包含了内存映射文件的页,可能有不必要的数据,占去了系统的内存。同样, Buffer Cache
,包括了从物理设备读或向物理设备写的数据,也可能包含了无用的缓冲。当系统中的物理页将要耗尽的时候,废弃这些缓存区中的页相对比较容易,因为它不需要向物理设备写(不象将页从内存中交换出去)。废弃这些页不会产生多少有害的副作用,只不过使访问物理设备和内存映射文件时慢一点。虽然如此,如果公平地废弃这些缓存区中的页,所有的进程受到的影响就是平等的。
每一次当核心交换进程要缩小这些缓存区时,它要检查 mem_map
页矢量中的页块,看是否可以从物理内存中废弃。如果系统空闲页太低(比较危险时)而核心交换进程交换比较厉害,这个检查的页块大小就会更大一些。页块的大小进行循环检查:每一次试图减少内存映射时都用一个不同的页块大小。这叫做
clock 算法,就象钟的时针。整个 mem_map 页向量都被检查,每次一些页。
参见 mm/filemap.c shrink_map()
检查的每一页都要判断缓存在 page cache 或者 buffer cache
中。注意共享页的废弃这时不考虑,一页不会同时在两个缓存中。如果该页不在这两个缓冲区中,则
mem_map
页向量表的下一页被检查。
缓存在 buffer cache ch
中的页(或者说页中的缓冲区被缓存)使缓冲区的分配和释放更有效。缩小内存映射的代码试图释放包含检查过的页的缓冲区。如果缓冲区释放了,则包含缓冲区的页也被释放了。如果检查的页是在
Linux 的 page cache 中,它将从 page cache 中删除并释放。
参见 fs/buffer.c free_buffer()
如果这次尝试释放了足够的页,核心交换进程就会继续等待直到下一次被周期性地唤醒。因为释放的页不属于任何进程的虚拟内存(只是缓存的页),因此不需要更新进程的页表。如果废弃的缓存页仍然不够,交换进程会试图交换出一些共享页。
8.2 Swapping Out System V Shared Memory Pages (交换出系统 V
的共享内存页)
系统 V
的共享内存是一种进程间通讯的机制,通过两个或多个进程共享虚拟内存交换信息。进程间如何共享内存在第
5 章详细讨论。现在只要讲讲每一块系统 V 共享内存都用一个
shmid_ds 的数据结构描述就足够了。它包括一个指向 vm_area_struct
链表数据结构的指针,用于共享此内存的每一个进程。 Vm_area_struct
数据结构描述了此共享内存在每一个进程中的位置。这个系统 V 的内存中的每一个 vm_area_struct 结构都用
vm_next_shared 和 vm_prev_shared 指针连接在一起。每一个 shmid_ds
数据结构都有一个页表条目的链表,每一个条目都描述一个共享的虚拟页和物理页的对应关系。
核心交换进程将系统 V 的共享内存页交换出去时也用 clock
算法。它每一次运行都记录了上一次交换出去了那一块共享内存的那一页。它用两个索引来记录:第一个是
shmid_ds
数据结构数组中的索引,第二个是这块共享内存区的页表链中的索引。这样可以共享内存区的牺牲比较公平。
参见 ipc/shm.c shm_swap()
因为一个指定的系统 V
共享内存的虚拟页对应的物理页号包含在每一个共享这块虚拟内存的进程的页表中,所以核心交换进程必须修改所有的进程的页表来体现此页已经不在内存而在交换文件中。对于每一个交换出去的共享页,交换进程必须找到在每一个共享进程的页表中对应的此页的条目(通过查找每一个
vm_area_struct
指针)如果在一个进程页表中此共享内存页的条目有效,交换进程要把它变为无效,并且标记是交换页,同时将此共享页的在用数减
1 。交换出去的系统 V
共享页表的格式包括一个在 shmid_ds
数据结构组中的索引和在此共享内存区中页表条目的索引。
如果所有共享的内存都修改过,页的在用数变为 0 ,这个共享页就可以写到交换文件中。这个系统 V
共享内存区的 shmid_ds
数据结构指向的页表中此页的条目将会换成交换出的页表条目。交换出的页表条目无效但是包含一个指向打开的交换文件的索引和此页在此文件内的偏移量。这个信息用于将此页再取回物理内存中。
8.2 Swapping Out and Discarding
Pages
交换进程轮流检查系统中的每一个进程是否可以用于交换。好的候选是可以交换的进程(有一些不行)并且有可以从内存中交换出去或废弃的一个或多个页。只有其他方法都不行的时候才会把页从物理内存交换到系统交换文件中。
参见 mm/vmscan.c swap_out()
来自于映像文件的执行映像的大部分内容可以从文件中重新读出来。例如:一个映像的执行指令不会被自身改变,所以不需要写到交换文件中。这些页只是被简单地废弃。如果再次被进程引用,可以从执行映像再次加载到内存中。
一旦要交换的进程确定下来,交换进程就查看它的所有虚拟内存区域,寻找没有共享或锁定的区域。
Linux 不会把选定进程的所有可以交换出去的页都交换出去,而只是去掉少量的页。如果页在内存中锁定,则不能被交换或废弃。
参见
mm/vmscan.c swap_out_vme() 跟踪进程 mm_struct 中排列的 vm_area_struct 结构中的
vm_next vm_nex 指针。
Linux 的交换算法使用了页的年龄。每一个页都有一个计数器(放在 mem_map_t
数据结构中),告诉核心交换进程此页是否值得交换出去。页不用时变老,访问时更新。交换进程只交换老的页。缺省地,页第一次分配时年龄赋值为
3 。每一次访问,它的年龄就增加 3 ,直到
20 。每一次系统交换进程运行时它将页的年龄减 1
使页变老。这个缺省的行为可以更改,所以这些信息(和其他相关信息)都存放在 swap_control
数据结构中。
如果页太老 ( 年龄
age = 0) ,交换进程会进一步处理。脏页可以交换出去, Linux 在描述此页的 PTE
中用一个和体系结构相关的位来描述这种页(见图 3.2
)。但是,并非所有的脏页都需要写到交换文件。每一个进程的虚拟内存区域都可以拥有自己的交换操作(由
vm_area_struct 中的 vm_ops
指针指示),如果这样,交换进程会用它的这种方式。否则,交换进程会从交换文件中分配一页,并把此页写到该文件中。
此页的页表条目会用一个无效的条目替换,但是包括了此页在交换文件的信息:此页所在文件内的偏移和所用的交换文件。不管什么方式交换,原来的物理页被放回到 free_area
重释放。干净(或不脏)的页可以被废弃,放回到 free_area 中重用。
如果交换或废弃了足够的可交换进程的页,交换进程重新睡眠。下一次唤醒时它会考虑系统中的下一个进程。这样,交换进程轻咬去每一个进程的物理页,直到系统重新达到平衡。这种做法比交换出整个进程更公平。
9 The Swap Cache (交换缓存)
当把页交换到交换文件时,
Linux
会避免写不必要写的页。有时可能一个页同时存在于交换文件和物理内存中。这发生于一页被交换出内存然后在进程要访问时又被调入内存的情况下。只要内存中的页没有被写过,交换文件中的拷贝就继续有效。
Linux
用 swap cache 来记录这些页。交换缓存是一个页表条目或者系统物理页的链表。一个交换页有一个页表条目,描述使用的交换文件和它在交换文件中的位置。如果交换缓存条目非
0 ,表示在交换文件中的一页没有被改动。如果此页后来被改动了(被写),它的条目就从交换缓存中删除)
当 Linux
需要交换一个物理页到交换文件的时候,它查看交换缓存,如果有此页的有效条目,它不需要把此页写到交换文件。因为内存中的此页从上次读到交换文件之后没有被修改过。
交换缓存中的条目是曾经交换出去的页表条目。它们被标记为无效,但是包含了允许 Linux
找到正确交换文件和交换文件中正确页的信息。
10 Swapping Page In
(交换进)
保存在交换文件中的脏页可能又需要访问。例如:当应用程序要向虚拟内存中写数据,而此页对应的物理页交换到了交换文件时。访问不在物理内存的虚拟内存页会引发
page fault 。 Page fault
是处理器通知操作系统它不能将虚拟内存转换到物理内存的信号。因为交换出去后虚拟内存中描述此页的页表条目被标记为无效。处理器无法处理虚拟地址到物理地址的转换,将控制转回到操作系统,告诉它发生错误的虚拟地址和错误的原因。这个信息的格式和处理器如何把控制转回到操作系统是和处理器类型相关的。处理器相关的
page faule 处理代码必须定位描述包括出错虚拟地址的虚拟内存区的 vm_area_struct
的数据结构。它通过查找该进程的 vm_area_struct
数据结构,直到找到包含了出错的虚拟地址的那一个。这是对时间要求非常严格的代码,所以一个进程的
vm_area_struct 数据结构按照特定的方式排列,使这种查找花费时间尽量少。
参见 arch/i386/mm/fault.c do_page_fault()
执行了合适的和处理器相关的动作并找到了包括错误(发生)的虚拟地址的有效的虚拟内存,
page fault 的处理过程又成为通用的,并可用于 Linux 能运行的所有处理器。通用的 page fault 处理代码查找错误虚拟地址的页表条目。如果它找到的页表条目是交换出去的页, Linux
必须把此页交换回物理内存。交换出去的页的页表条目的格式和处理器相关,但是所有的处理器都将这些页标为无效并在页表条目中放进了在交换文件中定位页的必要信息。
Linux 使用这种信息把此页调回到物理内存中。
参见 mm/memory.c
do_no_page()
这时, Linux
知道了错误(发生)的虚拟地址和关于此页交换到哪里去的页表条目。 Vm_area_struct
数据结构可能包括一个例程的指针,用于把这块虚拟内存中的页交换回到物理内存中。这是 swapin
操作。如果这块内存中有 swapin 操作,
Linux 会使用它。其实,交换出去的系统 V 的共享内存之所以需要特殊的处理因为交换的系统 V
的共享内存页的格式和普通交换页的不同。如果没有 swapin 操作, Linux
假定这是一个普通页,不需要特殊的处理。它分配一块空闲的物理页并将交换出去的页从交换文件中读进来。关于从交换文件哪里(和哪一个交换文件)的信息取自无效的页表条目。
参见 mm/page_alloc.c swap_in()
如果引起 page fault
的访问不是写访问,页就留在交换缓存中,它的页表条目标记为不可写。如果后来此页又被写,会产生另一个
page fault
,这时,此页被标志为脏页,而它的条目也从交换缓存中删除。如果此页没有被修改而又需要交换出来,
Linux
就可以避免将此页写到交换文件,因为此页已经在交换文件中了。
如果将此页从交换文件调回的访问是写访问,这个页就从交换缓存中删除,此页的页表条目页标记为脏页和可写。