页面错误 - os 如何在辅助存储中搜索页面?
Page Fault - How does os search for the page in secondary storage?
我的问题是当发生页面错误并且所需页面不在 RAM 中时,os 如何知道在整个辅助内存中的何处查找给定页面以将其带到内存?那么逻辑地址是辅助内存存储的地址还是需要的辅助存储地址存储在页面table本身或其他方式?
我觉得我可能在这里遗漏了一些非常基本的东西,但我想到了这个疑问,并且快速 google 搜索没有提供任何答案。
My question is when a page fault occurs and the required page is not in RAM ,after that how does the os know where to look for the given page in the entire secondary memory to bring it to the RAM?
如果有 50 种不同的操作系统,每种操作系统平均支持 10 种不同的体系结构,那么最多会有 500 个不同的答案;其中一个答案是“所有软件都使用物理地址,没有虚拟内存,也没有辅助内存”,另一个答案是“虚拟地址是磁盘上的一个位置,RAM 仅用作磁盘缓存以加快速度”(参见https://en.wikipedia.org/wiki/Single-level_store)。
对于大多数典型体系结构上的大多数典型现代操作系统运行;如果你计算出内核需要知道的关于每个虚拟页面的所有信息(例如页面假装是什么,页面实际上是什么,在磁盘上的位置(如果有的话),在 RAM 中的位置(如果有的话),要跟踪的东西"least recently used",要跟踪 "number of copy-on-write copies",等等);那么您可以将所有信息分散到多个不同的数据结构中,这样:
- 有些数据结构 used/required 由 CPU 本身提供,有些则不是
- 相同的信息可能同时也可能不在 2 个或多个数据结构中
- 有些数据结构对每个虚拟页面都有一个条目,而有些只对多个页面的每个范围都有一个条目
- 有些数据结构是 arrays/tables,有些是树,有些是 table 的树,还有一些是其他东西。
- 一些使用"virtual address"或"virtual page number"作为查找信息的关键字;有些使用其他东西(例如 PowerPC 和 Itanium 上的倒页 tables 使用 "physical address" 作为索引,因为使用你试图找到的索引是你可能做的最不明智的事情,那为什么不呢?)。
- 有些数据结构可能在内核中,有些可能不在内核中(例如,L4 微内核通过 "abstract hierarchical address space" 模型纯粹在用户 space 中管理虚拟内存映射) .
一般;有关页面数据在(每个不同的一块?)辅助存储器(如果有辅助存储器)中的位置的信息将存储在一个或多个地方的一个或多个地方。
请注意,当页面错误发生时,页面错误处理程序通常需要做出多个决定;可能首先弄清楚是什么导致了访问(一个进程,内核本身?)以及是否应该允许或拒绝访问,然后弄清楚如何处理它(发送 SIGSEGV
?做一个内核恐慌?获取数据到 CPU 的 TLB?使来自 CPU 的 TLB 的陈旧数据无效?进行写时复制克隆?从交换 space 获取数据?从文件获取数据?);所以页面错误处理程序最终会从(可能)多个不同的地方找到多个不同的数据片段。
具体示例
对于我的 OS 设计(基于异步消息传递并使用微内核);微内核足够小,可以针对特定架构进行定制设计和优化(不考虑可移植性)。操作系统设计用于分布式系统,因此不支持共享内存(和 fork()
)(您不希望页面错误处理程序必须通过拥挤的网络连接从远程计算机获取数据做一个 "copy on write"); "copy on write" 的唯一情况是内存映射文件,其中页面由一个或多个进程和(本地)VFS 缓存共享。
对于64位80x86,CPU需要一个4层tables的树(页tables,页目录,页目录指针table s 和 page map level 4),并为了提高效率(减少内存消耗和减少缓存未命中等)我尽可能使用这些 tables。
页面 table 条目(或页面目录条目,如果正在使用 2 MiB 页面);如果页面不存在,则 CPU 会忽略 63 位,OS 可以将其用于自己的目的;如果页面存在,则(取决于 CPU 支持的功能)至少有 9 位 OS 可以用于其自己的目的和 CPU 使用的标志(例如"read, write, no-execute" 标志)可用于增加 OS 自己的信息。
当页面不存在时,63 位被分成 2 个字段 - 一个 8 位字段用于跟踪页面的虚拟类型(如果它应该像 RAM,如果它应该是executable,如果它应该使用 "write-back" 缓存等)和一个 55 位 "where" 字段。如果 "where" 字段中的最高位被设置,页面被发送到交换 space 并且其他 54 位是 "swap space handle" (允许最大“2**54 * 4 KiB" 交换 space);如果 "where" 字段中的最高位已清除,则其他 54 位为 "memory mapped file handle"。如果由于 "not present" 页面而发生页面错误,则页面错误处理程序使用 8 位字段来确定是否应该允许或拒绝访问(或者由于不同的线程已经访问它,它是否已经被处理) ),然后(如果应该允许访问)页面错误处理程序告诉调度程序将线程置于 "WAITING FOR PAGE" 状态并将该页面标记为 "being fetched" (以便属于同一页面的其他线程process 知道它已经被获取了),然后使用 "where" 字段发送请求消息请求页面的数据到 Swap Manager(这是 user-space 中的一个进程),或者在内核 space 中找到一个 "memory mapped file descriptor" 结构,其中包含更多信息(不适合页面 table 条目)以确定页面在文件和文件句柄中的偏移量,并向 VFS 发送页面数据请求(VFS 或虚拟文件系统是 user-space 中的另一个进程)。之后;当 Swap Manager 或 VFS 向内核发送包含页面数据的回复消息时,内核修复页面 table 条目(将消息中的数据页面放入虚拟地址 space)和告诉调度程序解除 thread/s 的阻塞(将它们从 "WAITING FOR PAGE" 状态转移到 "READY TO RUN" 状态)。
对于这两种情况(内存映射文件和交换 space),如果访问是 "allowed read",则页面被映射为只读(无论页面是否应该是可写)。如果访问是 "allowed write",或者如果稍后对先前获取并映射为只读的页面进行了 "allowed write";然后,如果页面的数据来自 swap space,则页面错误处理程序通知 Swap Manager,可以丢弃 swap space 中的页面副本(如果同一页面被删除,则不能重新使用)稍后发送到交换 space),并且如果页面的数据来自内存映射文件,则页面错误处理程序通知 VFS 具有该页面副本的进程减少了,并复制了 "copy on write" 页面的数据到新分配的页面。
当页面为"present"时,它可能仍然是内存映射文件的一部分,并且在交换中可能仍有副本space;但是页面 table 条目中没有足够的 space 来存储 "where" 字段。在这种情况下,如果页面在交换 space 和 RAM 中,交换管理器必须接受 "Process ID + virtual address" 而不是 "swap space handle" (这会在交换管理器中造成一些额外的开销,因为它必须将 "Process ID + virtual address" 转换为 "swap space handle" 本身)。如果页面是 "copy on write" 内存映射文件,则页面错误处理程序会搜索进程的 "memory mapped file descriptors" 列表(这会导致一些额外的开销)。
注意(理论上)当 OS 的空闲 RAM 运行 不足时,它想要 select 一个 "least likely to be needed soon" 页面发送到交换 space,但这不是 easy/practical,因此大多数操作系统改用 "least recently used"。
我的内核根本不这样做。相反,他们只是将 "random" 页面发送到交换管理器,并且(最初)交换管理器将数据保存在 RAM 中,并且不会将其发送到任何交换提供者进行存储;交换管理器使用 "least recently sent to swap manager" 来确定将哪些页面发送给交换提供者进行存储。经常使用的页面可能会多次发送到交换管理器,而实际上从未发送到交换提供者(并且不会导致频繁使用的页面的磁盘 IO 速度变慢)。还要注意,因为 "copy on write memory mapped file" 是唯一使用 "copy on write" 的情况,并且因为没有其他形式的共享内存,VFS 可以跟踪有多少进程正在共享页面本身的副本,并且内核永远不需要跟踪有多少进程正在共享任何页面的副本(就像大多数操作系统的大多数内核一样)。
我的问题是当发生页面错误并且所需页面不在 RAM 中时,os 如何知道在整个辅助内存中的何处查找给定页面以将其带到内存?那么逻辑地址是辅助内存存储的地址还是需要的辅助存储地址存储在页面table本身或其他方式? 我觉得我可能在这里遗漏了一些非常基本的东西,但我想到了这个疑问,并且快速 google 搜索没有提供任何答案。
My question is when a page fault occurs and the required page is not in RAM ,after that how does the os know where to look for the given page in the entire secondary memory to bring it to the RAM?
如果有 50 种不同的操作系统,每种操作系统平均支持 10 种不同的体系结构,那么最多会有 500 个不同的答案;其中一个答案是“所有软件都使用物理地址,没有虚拟内存,也没有辅助内存”,另一个答案是“虚拟地址是磁盘上的一个位置,RAM 仅用作磁盘缓存以加快速度”(参见https://en.wikipedia.org/wiki/Single-level_store)。
对于大多数典型体系结构上的大多数典型现代操作系统运行;如果你计算出内核需要知道的关于每个虚拟页面的所有信息(例如页面假装是什么,页面实际上是什么,在磁盘上的位置(如果有的话),在 RAM 中的位置(如果有的话),要跟踪的东西"least recently used",要跟踪 "number of copy-on-write copies",等等);那么您可以将所有信息分散到多个不同的数据结构中,这样:
- 有些数据结构 used/required 由 CPU 本身提供,有些则不是
- 相同的信息可能同时也可能不在 2 个或多个数据结构中
- 有些数据结构对每个虚拟页面都有一个条目,而有些只对多个页面的每个范围都有一个条目
- 有些数据结构是 arrays/tables,有些是树,有些是 table 的树,还有一些是其他东西。
- 一些使用"virtual address"或"virtual page number"作为查找信息的关键字;有些使用其他东西(例如 PowerPC 和 Itanium 上的倒页 tables 使用 "physical address" 作为索引,因为使用你试图找到的索引是你可能做的最不明智的事情,那为什么不呢?)。
- 有些数据结构可能在内核中,有些可能不在内核中(例如,L4 微内核通过 "abstract hierarchical address space" 模型纯粹在用户 space 中管理虚拟内存映射) .
一般;有关页面数据在(每个不同的一块?)辅助存储器(如果有辅助存储器)中的位置的信息将存储在一个或多个地方的一个或多个地方。
请注意,当页面错误发生时,页面错误处理程序通常需要做出多个决定;可能首先弄清楚是什么导致了访问(一个进程,内核本身?)以及是否应该允许或拒绝访问,然后弄清楚如何处理它(发送 SIGSEGV
?做一个内核恐慌?获取数据到 CPU 的 TLB?使来自 CPU 的 TLB 的陈旧数据无效?进行写时复制克隆?从交换 space 获取数据?从文件获取数据?);所以页面错误处理程序最终会从(可能)多个不同的地方找到多个不同的数据片段。
具体示例
对于我的 OS 设计(基于异步消息传递并使用微内核);微内核足够小,可以针对特定架构进行定制设计和优化(不考虑可移植性)。操作系统设计用于分布式系统,因此不支持共享内存(和 fork()
)(您不希望页面错误处理程序必须通过拥挤的网络连接从远程计算机获取数据做一个 "copy on write"); "copy on write" 的唯一情况是内存映射文件,其中页面由一个或多个进程和(本地)VFS 缓存共享。
对于64位80x86,CPU需要一个4层tables的树(页tables,页目录,页目录指针table s 和 page map level 4),并为了提高效率(减少内存消耗和减少缓存未命中等)我尽可能使用这些 tables。
页面 table 条目(或页面目录条目,如果正在使用 2 MiB 页面);如果页面不存在,则 CPU 会忽略 63 位,OS 可以将其用于自己的目的;如果页面存在,则(取决于 CPU 支持的功能)至少有 9 位 OS 可以用于其自己的目的和 CPU 使用的标志(例如"read, write, no-execute" 标志)可用于增加 OS 自己的信息。
当页面不存在时,63 位被分成 2 个字段 - 一个 8 位字段用于跟踪页面的虚拟类型(如果它应该像 RAM,如果它应该是executable,如果它应该使用 "write-back" 缓存等)和一个 55 位 "where" 字段。如果 "where" 字段中的最高位被设置,页面被发送到交换 space 并且其他 54 位是 "swap space handle" (允许最大“2**54 * 4 KiB" 交换 space);如果 "where" 字段中的最高位已清除,则其他 54 位为 "memory mapped file handle"。如果由于 "not present" 页面而发生页面错误,则页面错误处理程序使用 8 位字段来确定是否应该允许或拒绝访问(或者由于不同的线程已经访问它,它是否已经被处理) ),然后(如果应该允许访问)页面错误处理程序告诉调度程序将线程置于 "WAITING FOR PAGE" 状态并将该页面标记为 "being fetched" (以便属于同一页面的其他线程process 知道它已经被获取了),然后使用 "where" 字段发送请求消息请求页面的数据到 Swap Manager(这是 user-space 中的一个进程),或者在内核 space 中找到一个 "memory mapped file descriptor" 结构,其中包含更多信息(不适合页面 table 条目)以确定页面在文件和文件句柄中的偏移量,并向 VFS 发送页面数据请求(VFS 或虚拟文件系统是 user-space 中的另一个进程)。之后;当 Swap Manager 或 VFS 向内核发送包含页面数据的回复消息时,内核修复页面 table 条目(将消息中的数据页面放入虚拟地址 space)和告诉调度程序解除 thread/s 的阻塞(将它们从 "WAITING FOR PAGE" 状态转移到 "READY TO RUN" 状态)。
对于这两种情况(内存映射文件和交换 space),如果访问是 "allowed read",则页面被映射为只读(无论页面是否应该是可写)。如果访问是 "allowed write",或者如果稍后对先前获取并映射为只读的页面进行了 "allowed write";然后,如果页面的数据来自 swap space,则页面错误处理程序通知 Swap Manager,可以丢弃 swap space 中的页面副本(如果同一页面被删除,则不能重新使用)稍后发送到交换 space),并且如果页面的数据来自内存映射文件,则页面错误处理程序通知 VFS 具有该页面副本的进程减少了,并复制了 "copy on write" 页面的数据到新分配的页面。
当页面为"present"时,它可能仍然是内存映射文件的一部分,并且在交换中可能仍有副本space;但是页面 table 条目中没有足够的 space 来存储 "where" 字段。在这种情况下,如果页面在交换 space 和 RAM 中,交换管理器必须接受 "Process ID + virtual address" 而不是 "swap space handle" (这会在交换管理器中造成一些额外的开销,因为它必须将 "Process ID + virtual address" 转换为 "swap space handle" 本身)。如果页面是 "copy on write" 内存映射文件,则页面错误处理程序会搜索进程的 "memory mapped file descriptors" 列表(这会导致一些额外的开销)。
注意(理论上)当 OS 的空闲 RAM 运行 不足时,它想要 select 一个 "least likely to be needed soon" 页面发送到交换 space,但这不是 easy/practical,因此大多数操作系统改用 "least recently used"。 我的内核根本不这样做。相反,他们只是将 "random" 页面发送到交换管理器,并且(最初)交换管理器将数据保存在 RAM 中,并且不会将其发送到任何交换提供者进行存储;交换管理器使用 "least recently sent to swap manager" 来确定将哪些页面发送给交换提供者进行存储。经常使用的页面可能会多次发送到交换管理器,而实际上从未发送到交换提供者(并且不会导致频繁使用的页面的磁盘 IO 速度变慢)。还要注意,因为 "copy on write memory mapped file" 是唯一使用 "copy on write" 的情况,并且因为没有其他形式的共享内存,VFS 可以跟踪有多少进程正在共享页面本身的副本,并且内核永远不需要跟踪有多少进程正在共享任何页面的副本(就像大多数操作系统的大多数内核一样)。