本文转载自微信公众号《后端技术罗盘》,作者程序员大白。转载请联系公众号。1、大家好!上周大白因为一些事停了一次更新。最近在思考如何让大家在10分钟内有所收获,于是打算创作一个系列《什么是xxx》,写一些简洁的知识点。抛出一个关于阿里的面试题来给大家预热一下,引出今天的主角——PageFault。谈谈对PageFault的理解。话不多说,我们上车吧。2.TerminologyConventionVA:VirtualAddressVirtualAddressPA:PhysicalAddressPhysicalAddressMMU:内存管理单元TLB:TranslationLookasideBufferBypassFastTableCache/AddressTranslationCachePTE:PageTableEntryPagingTableEntry3.Memory懒分配32-bitLinux系统为例。每个进程独立拥有4GB的虚拟地址空间。根据局部性原则,没有必要也不可能为每个进程分配4GB的物理地址空间。64位系统也是一样,只是空间寻址范围大了很多倍,进程的虚拟地址空间会被分成几个部分:其实只有当程序运行的时候才会用到就去内存中找虚拟地址对应的pageframe,找不到才能分配。这就是内存的惰性(延迟)分配机制。对于一个正在运行的进程,并不是所有的虚拟地址在物理内存中都有相应的页面。图中显示了一些虚拟地址有对应的物理页的情况:虚拟地址空间一般按照固定大小4KB划分,物理内存可以设置不同的页大小。通常,物理页面大小与虚拟页面大小相同。本文基于4KB的物理页面大小。经过前面的分析,我们会面临一个问题:如何准确快速地将虚拟地址映射到物理页面?>>>高能预警敲黑板很大但只是实际对应物理空间的一小部分。2.内存惰性分配是保证内存利用率和服务器利用率的有效机制,是一种合理分配资源的方法。3.快速准确查询大量虚拟地址并将其转换为物理地址是一个难题。4、如果CPU获取内存中的数据,CPU并不直接与物理内存打交道,而是将地址转换的工作外包给MMU。MMU是一个高速的硬件电路。主要工作是进行内存管理,地址转换只是它所承担的业务之一。让我们看一下MMU是如何处理地址转换的。4.1MMU和页表每个进程都会有自己的页表PageTable。页表存储了进程中虚拟地址到物理地址的映射关系,所以相当于一个映射。MMU收到CPU的虚拟地址后,开始查询Pagetable,判断是否存在映射,读写权限是否正常,如图:对于4GB的虚拟地址,页面大小为4KB,一级页表会有2^20个表项,页表占用连续内存,存储空间大,多级页表可以有效降低页表的存储空间和内存连续性要求,但多级页表也带来查询效率问题。我们以二级页表为例。MMU需要执行两次页表查询来确定物理地址。确认权限等问题后,MMU将物理地址发送到总线。内存收到后,开始读取对应的地址。数据并返回。MMU在2级页表的情况下进行2次查找和1次读写,所以当页表变成N级时,就变成了N次查找+1次读写。可以看出,页表层级数越多,查询的步数越多,CPU等待时间越长,效率越低。这个问题还是需要优化的。>>本段小结,敲黑板画重点<<1.页表存在于进程的内存中。MMU收到虚拟地址后,查询PageTable获取物理地址。2.单级页表对连续内存要求高,所以引入了多级页表,但是多级页表也是一把双刃剑,降低查询效率的同时降低了连续存储要求和存储空间。4.2MMU和TLB的故事MMU和TLB的故事是这样开始的……CPU觉得MMU虽然辛苦,但是效率有点低,不想继续外包给它。这顿时让MMU急了。于是MMU找了一些精通统计学的朋友。经过一番研究发现,CPU使用的数据往往是少量的,但是每次MMU都要重复前面的步骤去取回。方法和方法也得讲究!找到瓶颈后,MMU推出了新武器,即世人称之为快表的TLB。TLB虽然容量不大,但是正式上岗之后上班真的不含糊。CPU将新的虚拟地址发送给MMU后,MMU先询问TLB是否有,如果有则直接获取物理地址发送到总线到内存,共同工作。TLB的容量比较小,难免会出现CacheMiss。这个时候MMU还有一个保底的老武器页表PageTable。MMU在页表中找到后,不仅将总线地址发送给内存,还将这个映射关系交给TLB,让它记录刷新缓存。当TLB容量未满时,直接存储新记录。满了的时候,用淘汰法清除旧记录,保存新记录,貌似完美解决了问题。有了TLB和PageTable的支持,最近CPU感觉MMU厉害了,就问MMU是怎么做到的?MMU准确地告诉了CPU。CPU说这是个好办法,然后给出了自己的建议:TLB还是有点小,cachemiss经常发生,是不是要建大一点的,这样才能更快的访问更多的存储?MMU苦笑道,大哥TLB好贵,怎么不增加外包费?话还没说完,CPU就说涨工资不可能了,这辈子都不可能了。>>>高能敲黑板预警本段内容提要<<<1.CPU需要根据用户进程提供的虚拟地址获取真实数据,但它自己不做而是交给了MMU。2.MMU也是个聪明人。它集成了一个TLB来存储CPU最近使用的页表条目,以加快寻址速度。如果找不到TLB,那就去全页表寻址。可以认为TLB就是MMU的缓存。3、TLB的容量毕竟是有限的,所以需要依靠PageTable来完成TLBMiss情况的查询,更新到TLB建立新的映射关系。5.揭开PageFault的秘密。想象一下,CPU给MMU的虚拟地址在TLB或PageTable中没有找到对应的物理页框,或者权限不对。我应该怎么办?是的,这就是页面错误。是由硬件中断触发的错误,可以通过软件逻辑纠正。5.1PageFault,它来了如果目标内存页在物理内存中没有对应的页框或者存在但没有对应的权限,CPU就无法获取到数据。在这种情况下,CPU将报告页面错误。由于CPU在没有数据的情况下无法进行计算,当CPU发生罢工时,用户进程就会被页面错误中断。进程会从用户态切换到内核态,缺页中断会交给内核的PageFaultHandler处理。页错误异常并不可怕。只要CPU需要的虚拟地址经过MMU的一些寻址后没有找到或者找到后没有权限,就会出现pagefault异常。因此异常触发后的处理流程将是重点内容。5.2页面错误的分类和处理页面错误会交给PageFaultHandler处理,它会根据不同类型的页面错误进行不同的处理:HardPageFault也称为MajorPageFault,译为硬页错误/主要错误页面过错。此时物理内存中没有对应的页框。CPU需要打开磁盘设备读入物理内存,然后让MMU建立VA和PA的映射关系。SoftPageFault也称为MinorPageFault,译为软页故障/minorpagefault。此时物理内存中有相应的页框,但可能被其他进程调用进来,抛出pagefault异常。该过程不知道它。此时MMU只需要建立映射即可。它不需要从磁盘读取和写入内存。一般出现在多进程共享内存区。InvalidPageFault翻译为无效页错误。比如进程访问的内存地址越界,比如空指针被解引用,内核会报segmentfault错误中断进程,直接挂掉。5.3页面错误的原因不同类型的页面错误有不同的原因。常见的原因有:非法操作和越界访问这种情况影响最大,也是Coredump的重要来源,比如空指针解决方案可能会因为引用或者权限问题导致Pagefaults。使用malloc申请新内存malloc机制是以延迟的方式分配内存。使用malloc申请内存时,实际上并没有分配物理内存。当真正找到malloc申请的物理内存时,就会启动应用程序,这期间会出现PageFault。访问数据通过swap从物理内存中换出,这是一种有限的资源。当许多进程在运行时,并不是每个进程都处于活动状态。为此,OS会启动内存页面置换,将长期不用的物理内存页框放入swap分区,以释放资源。对于其他进程,当访问交换分区中存在的页面时,将触发PageFault并将其替换回物理内存。>>>敲黑板划重点本段小结:<<<触发PageFault的原因可能有很多,归根结底只有几大类:1.如果共享内存area使用,没有VA->PA的映射但是有对于物理页框的软页错误,只要在PageTable/TLB中建立映射关系即可。2、要访问的地址在物理内存中不存在,需要从disk/swap分区读取才能使用。这个性能影响会比较大,因为磁盘太慢了,尽量使用高性能的SSD来降低延迟。3.被访问的地址内存非法,pagefault会升级并触发SIGSEGV信号结束进程。这是一种页面错误,会导致进程挂起。6.全文小结本文简单跟大家学习了PageFault的相关知识点,包括Linux虚拟地址和物理地址的关系,CPU获取内存数据的过程,MMU和TLB&页表的配合,以及异常页面错误。病因及分类治疗。本文不展开MMU的内部机制、内核态&用户态缺页异常、缺页异常处理函数等,主要是这部分内容比较晦涩,还得自己去研究。本文旨在引火烧身,而不是填满水桶。对于文章中的相关知识点,欢迎交流学习。
