监控进程的major page faults和minor page faults
场景
有时候遇到机器没有性能瓶颈,但是进程出现性能瓶颈,这时候往往需要进程级别的监控
监控手段:process_exporter
https://github.com/ncabatoff/process-exporter
监控相关指标:major_page_faults_total 和 minor_page_faults_total
-
major_page_faults_total counter
Number of major page faults based on /proc/[pid]/stat field majflt(12).
-
minor_page_faults_total counter
Number of minor page faults based on /proc/[pid]/stat field minflt(10).
major_page_faults 和 minor_page_faults
内存管理技术:
在现代计算中,分页和内存交换是用于内存分配和释放的两种常见内存管理技术。内存管理可用于减少程序地址空间的碎片,通过减少高速缓存未命中来提高性能,或通过防止缓冲区溢出来提高安全性。操作系统中的内存管理涉及应用程序和进程的内存分配和使用跟踪方法的组合。
内存管理单元(MMU)将逻辑地址映射到物理地址,同时管理执行期间进程在存储磁盘和主内存之间的移动。
Linux 通过将物理内存划分为页,然后将这些物理页映射到进程所需的虚拟内存来为进程分配内存。它与 CPU 中的内存管理单元 (MMU) 结合执行此操作。通常,一个页面代表 4KB 的物理内存。每个页面都会保存统计信息和标志,以告知 Linux 该内存块的状态。
页面错误:
操作系统使用分页在主内存和辅助存储之间传输数据,以实现高效的内存管理。页(内存/虚拟页)是运行进程的一部分,代表内存的逻辑单元。包含单个进程页的内存物理部分称为帧。所有帧都是固定长度的,允许非连续(非共享)地址空间分配。
页面错误是内存管理单元引发的异常,当进程需要访问其地址空间内的数据但无法加载到物理内存时,就会发生该异常。异常通常会指示机器在虚拟内存中找到该数据块,这样就可以将其从存储设备发送到物理内存。当操作系统的内核分配或拒绝对特定进程的 RAM 访问时,页错误处理通常是自动化的。页面错误很常见,并且通常有助于通过增加程序可用的内存量来提高性能。
想象一下 Linux 系统上正在运行的大型程序。程序可执行文件的大小可以以兆字节为单位来衡量,但并非所有代码都会立即运行。某些代码只会在初始化期间或发生特殊情况时运行。随着时间的推移,如果 Linux 认为不再需要或很少使用保存可执行代码的内存页面,则可以丢弃它们。因此,即使程序正在运行,也并非所有机器代码都会保存在内存中。程序由 CPU 在逐步执行机器代码时执行。每条指令都存储在物理内存中的某个地址。 MMU 处理从物理地址空间到虚拟地址空间的映射。在程序执行的某个时刻,CPU 可能需要寻址不在内存中的代码。 MMU 知道该代码的页面不可用(因为 Linux 告诉它),因此 CPU 将引发页面错误。
1、major_page_faults:可通过 Disk IO来满足
major_page_faults发生与处理:
当一个页面被引用并且它不存在于 RAM 中时,就会发生此错误,这会更糟糕,因为现在将该页面获取到逻辑地址空间将导致系统性能的严重损失。
首先,当进程找不到要执行的页面时,用户进程会停止并向内核发出陷阱,保存 cpu 上的易失性信息,检查 RAM 上的帧以查找该页面,但由于该页面不存在,因此现在开始执行繁重的工作将页面从辅助存储(磁盘)引入 RAM 并将其映射到页表中,但是如果 RAM 上没有空帧,情况会变得更糟,因为现在所需的页面将不会转到空闲帧列表,因为它不再有空间。现在需要将页面替换为 RAM 某一帧中已经存在的页面,但是等待它也可以从这里开始走下坡路,如果替换的页面是脏的怎么办,然后需要将其保存在磁盘上,保存后现在我们可以在框架上替换它并更新页表。
具体原因举例:
-
页面错误处理程序可能会尝试回收由于内存压力(物理内存不足)而较早换出的物理页面,从而导致磁盘 I/O 以便从配置的交换空间中读取先前换出的页面。
-
页面错误处理程序可能试图从刚刚经过 mmap() 处理的打开文件中读取数据。如果文件内容已不在页面缓存内,这也会导致磁盘 I/O
-
当进程尝试以超出其权限的方式访问内存时发生的异常。例如,如果程序尝试从物理内存的未映射区域访问数据,或者写入超出分配的虚拟地址空间的末尾,则会发生主要页面错误。
特点:
与minor_page_faults相比,它的性能损失非常大
2、minor_page_faults:可以直接利用内存中的缓存页满足
minor_page_faults发生与处理:
当进程没有到页面的逻辑映射,但该页面存在于 RAM 中的帧中时,就会发生这种情况。
在这种情况下,内核仅更新页表以引用所需的页面。
具体原因示例:
-
进程可能引用内存中的共享库,但该进程在其页表中没有到它的映射,在这种情况下,内核仅更新页表以引用所需的页面。
-
当一个页面从进程中回收并放入空闲帧列表中时,但该页面尚未清除前一个进程中的数据,在这种情况下,首先会清除该页面的数据,然后由内核分配给我们的进程。通常发生在程序尝试在堆上分配空间时。
-
当所需的代码(或数据)实际上已经在内存中但未分配给该进程时,就会发生这种情况。例如,如果用户正在运行 Web 浏览器,则具有浏览器可执行代码的内存页面可以在多个用户之间共享(因为二进制文件是只读的并且无法更改)。如果第二个用户启动同一个 Web 浏览器,那么 Linux 不会再次从磁盘加载所有二进制文件,它将映射第一个用户的可共享页面,并授予第二个进程访问它们的权限。换句话说,仅当更新页列表(并且配置了 MMU)而不实际需要访问磁盘时,才会发生次要页错误。
-
Copy on Write之延迟分配:可执行文件可以向 Linux 请求一些内存,例如 8 MB,以便它可以执行某些任务或其他任务。 Linux 实际上并没有为进程提供 8 MB 的物理内存。相反,它分配 8 MB 虚拟内存并将这些页面标记为“写入时复制”。这意味着,虽然它们未使用,但不需要实际物理分配它们,但当进程写入该页面时,就会分配一个真正的物理页面,并将该页面分配给该进程。写入时复制(COW)是一种内存管理技术,允许操作系统在多个进程之间共享物理内存。通过这种方法,每个进程都可以拥有自己的共享数据的私有视图,而无需为其分配新的内存。这使得 COW 成为减少虚拟内存使用和提高应用程序性能的极其有效的机制。
写入时复制虚拟内存管理技术通过允许父进程和子进程最初驻留在同一内存页中来帮助处理页面错误。一旦进程尝试修改共享页面,就会创建该页面的副本,因此只有修改才会影响活动进程。如果子进程没有被修改,它会继续作为父进程的引用而存在。
特点:
与major_page_faults相比,它的性能损失非常小
IO层级
对于IO子系统来说,内核中的分层结构从上到下:
VFS–> EXT4/EXT3–>Page Cache–> General Block Layer --> IO Scheduler --> Disk Driver
那么如果访问一个地址时,与该地址空间vma绑定的数据还存在于Disk上,那么此时即会触发一次major fault;如果访问一个地址时,与之绑定的vma对应的地址空间已经被内核加载到了Page Cache中,那么此时只需要把该Page映射到vma中即可,这种异常即为一次minor fault。
为什么数据已经被加载内核中的Page Cache了,理论上说直接访问就行了,为什么还要触发一次minor fault呢?
主要是因为虚拟地址和物理地址的映射关系并没有建立,Linux进程访问一块内存实际上使用的是虚拟内存,必须把对应虚拟地址空间和物理页面进行了映射才能够正常访问,那么vma结构体实际仅仅表示一个虚拟地址空间,必须把内核中Page Cache中的物理地址与进程vma虚拟地址空间进行映射才能正常被进程访问到。
总结
对于由用户空间的页面错误触发的页面请求,如果页面错误处理程序能够满足该请求而不引起磁盘 I/O,则将其视为次要页面错误。但是,如果页面错误处理程序必须引发磁盘 I/O 才能满足页面请求,则它将被视为主要页面错误
-
当 CPU 需要访问不在内存中的页面时,就会引发页面错误。
-
major page faults只能通过访问磁盘来解决。
-
minor page faults可以通过共享内存中已有的页面来解决