武汉PHP培训
达内武汉民大中心

15827352908

热门课程

武汉PHP培训丨程序员的自我修养:内存缺页错误

  • 时间:2018-01-31 15:51
  • 发布:武汉PHP培训
  • 来源:互联网

    众所周知,CPU 不能直接和硬盘进行交互.CPU 所作的一切运算,都是通过 CPU 缓存间接与内存进行操作的.若是 CPU 请求的内存数据在物理内存中不存在,那么 CPU 就会报告「缺页错误(Page Fault)」,提示内核.
    在内核处理缺页错误时,就有可能进行磁盘的读写操作.这样的操作,相对 CPU 的处理是非常缓慢的.因此,发生大量的缺页错误,势必会对程序的性能造成很大影响.因此,在对性能要求很高的环境下,应当尽可能避免这种情况.
    此篇介绍缺页错误本身,并结合一个实际示例作出一些实践分析.这里主要在 Linux 的场景下做讨论;其他现代操作系统,基本也是类似的.
    内存页和缺页错误
    分页模式
    我们在前作内存寻址中介绍了 CPU 发展过程中内存寻址方式的变化.现代 CPU 都支持分段和分页的内存寻址模式.出于寻址能力的考虑,现代操作系统,也顺应着都支持段页式的内存管理模式.当然,虽然支持段页式,但是 Linux 中只启用了段基址为 0 的段.也就是说,在 Linux 当中,实际起作用的只有分页模式.
    具体来说,分页模式在逻辑上将虚拟内存和物理内存同时等分成固定大小的块.这些块在虚拟内存上称之为「页」,而在物理内存上称之为「页帧」,并交由 CPU 中的 MMU 模块来负责页帧和页之间的映射管理.
    引入分页模式的好处,可以大致概括为两个方面:
    扩大了 CPU 的寻址空间大小.这是因为,以 4KiB 为页大小时,CPU 的寻址单位从 1Byte 增加到 4KiB;因此,寻址空间扩大了 4096 倍.对于 32bit 的地址总线,寻址空间就从 1MiB 扩大到了 4GiB.
    允许虚存空间远大于实际物理内存大小的情况.这是因为,分页之后,操作系统读入磁盘的文件时,无需以文件为单位全部读入,而可以以内存页为单位,分片读入.同时,考虑到 CPU 不可能一次性需要使用整个内存中的数据,因此可以交由特定的算法,进行内存调度:将长时间不用的页帧内的数据暂存到磁盘上.
    缺页错误
    当进程在进行一些计算时,CPU 会请求内存中存储的数据.在这个请求过程中,CPU 发出的地址是逻辑地址(虚拟地址),然后交由 CPU 当中的 MMU 单元进行内存寻址,找到实际物理内存上的内容.若是目标虚存空间中的内存页(因为某种原因),在物理内存中没有对应的页帧,那么 CPU 就无法获取数据.这种情况下,CPU 是无法进行计算的,于是它就会报告一个缺页错误(Page Fault).
    因为 CPU 无法继续进行进程请求的计算,并报告了缺页错误,用户进程必然就中断了.这样的中断称之为缺页中断.在报告 Page Fault 之后,进程会从用户态切换到系统态,交由操作系统内核的 Page Fault Handler 处理缺页错误.
    缺页错误的分类和处理

    基本来说,缺页错误可以分为两类:硬缺页错误(Hard Page Fault)和软缺页错误(Soft Page Fault).这里,前者又称为主要缺页错误(Major Page Fault);后者又称为次要缺页错误(Minor Page Fault).当缺页中断发生后,Page Fault Handler 会判断缺页的类型,进而处理缺页错误,最终将控制权交给用户态代码.

武汉PHP培训

    若是此时物理内存里,已经有一个页帧正是此时 CPU 请求的内存页,那么这是一个软缺页错误;于是,Page Fault Hander 会指示 MMU 建立相应的页帧到页的映射关系.这一操作的实质是进程间共享内存--比如动态库(共享对象),比如 mmap 的文件.
    若是此时物理内存中,没有相应的页帧,那么这就是一个硬缺页错误;于是 Page Fault Hander 会指示 CPU,从已经打开的磁盘文件中读取相应的内容到物理内存,而后交由 MMU 建立这份页帧到页的映射关系.
    不难发现,软缺页错误只是在内核态里轻轻地走了一遭,而硬缺页错误则涉及到磁盘 I/O.因此,处理起来,硬缺页错误要比软缺页错误耗时长得多.这就是为什么我们要求高性能程序必须在对外提供服务时,尽可能少地发生硬缺页错误.
    除了硬缺页错误和软缺页错误之外,还有一类缺页错误是因为访问非法内存引起的.前两类缺页错误中,进程尝试访问的虚存地址尚为合法有效的地址,只是对应的物理内存页帧没有在物理内存当中.后者则不然,进程尝试访问的虚存地址是非法无效的地址.比如尝试对 nullptr 解引用,就会访问地址为 0x0 的虚存地址,这是非法地址.此时 CPU 报出无效缺页错误(Invalid Page Fault).操作系统对无效缺页错误的处理各不相同:Windows 会使用异常机制向进程报告;*nix 则会通过向进程发送 SIGSEGV 信号(11),引发内存转储.
    缺页错误的原因
    之前提到,物理内存中没有 CPU 所需的页帧,就会引发缺页错误.这一现象背后的原因可能有很多.
    例如说,进程通过 mmap 系统调用,直接建立了磁盘文件和虚拟内存的映射关系.然而,在 mmap 调用之后,并不会立即从磁盘上读取这一文件.而是在实际需要文件内容时,通过 CPU 触发缺页错误,要求 Page Fault Handler 去将文件内容读入内存.
    又例如说,一个进程启动了很久,但是长时间没有活动.若是计算机处在很高的内存压力下,则操作系统会将这一进程长期未使用的页帧内容,从物理内存转储到磁盘上.这个过程称为换出(swap out).在 *nix 系统下,用于转储这部分内存内容的磁盘空间,称为交换空间;在 Windows 上,这部分磁盘空间,则被称为虚拟内存,对应磁盘上的文件则称为页面文件.在这个过程中,进程在内存中保存的任意内容,都可能被换出到交换空间:可以是数据内容,也可以是进程的代码段内容.
    Windows 用户看到这里,应该能明白这部分空间为什么叫做「虚拟内存」--因为它于真实的内存条相对,是在硬盘上虚拟出来的一份内存.通过这样的方式,「好像」将内存的容量扩大了.同样,为什么叫「页面文件」也一目了然.因为事实上,文件内保存的就是一个个内存页帧.在 Windows 上经常能观察到「假死」的现象,就和缺页错误有关.这种现象,实际就是长期不运行某个程序,导致程序对应的内存被换出到磁盘;在需要响应时,由于需要从磁盘上读取大量内容,导致响应很慢,产生假死现象.这种现象发生时,若是监控系统硬错误数量,就会发现在短时间内,目标进程产生了大量的硬错误.
    在 Windows XP 流行的年代,有很多来路不明的「系统优化建议」.其中一条就是「扩大页面文件的大小,有助于加快系统速度」.事实上,这种方式只能加大内存「看起来」的容量,却给内存整体(将物理内存和磁盘页面文件看做一个整体)的响应速度带来了巨大的负面影响.因为,尽管容量增大了,但是访问这部分增大的容量时,进程实际上需要先陷入内核态,从磁盘上读取内容做好映射,再继续执行.更有甚者,这些建议会要求「将页面文件分散在多个不同磁盘分区」,并美其名曰「分散压力」.事实上,从页面文件中读取内存页帧本就已经很慢;若是还要求磁盘不断在不同分区上寻址,那就更慢了.可见谣言害死人.
    观察缺页错误
    Windows 系统
    相对于任务管理器,Windows 的资源监视器知之者甚少.Windows 的资源监视器,可以实时显示一系列硬件、软件资源的适用情况.硬件资源包括 CPU、内存、磁盘和网络;软件资源则是文件句柄和模块.用户可以在启动窗口中,以 resmon.exe 启动资源监视器(Vista 里是 perfmon.exe).或是由开始按钮→所有程序→辅助程序→系统工具→资源监视器打开.
    在内存资源监视标签中,有「硬错误/秒」或者「硬中断/秒」的监控项.若是一直打开资源监视器,以该项降序排列所有进程,则在发现程序卡顿、假死时,能观察到大量硬错误爆发性产生.

    本篇文章是由武汉PHP培训为您呈现,希望给您带来更多更好的文章,喜欢的朋友们可以添加微信公众号.

更多武汉PHP培训相关咨询,请扫描下方二维码

武汉PHP培训

上一篇:武汉PHP培训丨两个程序员
下一篇:武汉PHP培训丨谈谈对程序员的培养

PHP 优化写法之字符串

CodeIgniter的优缺点

PHP高性能框架之一Yii

选择城市和中心
贵州省

广西省

海南省

有位老师想和您聊一聊