linux 缺页?linux无法定位软件包

老铁们,大家好,相信还有很多朋友对于linux 缺页和linux无法定位软件包的相关问题不太懂,没关系,今天就由我来为大家分享分享linux 缺页以及linux无法定位软件包的问题,文章篇幅可能偏长,希望可以帮助到大家,下面一起来看看吧!

linux缺页异常处理三步骤

在程序的执行过程中,因为遇到某种障碍而使 CPU无法最终访问到相应的物理内存单元,即无法完成从虚拟地址到物理地址映射的时候,CPU会产生一次缺页异常,从而进行相应的缺页异常处理。基于 CPU的这一特性,Linux采用了请求调页(Demand Paging)和写时复制(Copy On Write)的技术

1.请求调页是一种动态内存分配技术,它把页框的分配推迟到不能再推迟为止。这种技术的动机是:进程开始运行的时候并不访问地址空间中的全部内容。事实上,有一部分地址也许永远也不会被进程所使用。程序的局部性原理也保证了在程序执行的每个阶段,真正使用的进程页只有一小部分,对于临时用不到的页,其所在的页框可以由其它进程使用。因此,请求分页技术增加了系统中的空闲页框的平均数,使内存得到了很好的利用。从另外一个角度来看,在不改变内存大小的情况下,请求分页能够提高系统的吞吐量。当进程要访问的页不在内存中的时候,就通过缺页异常处理将所需页调入内存中。

2.写时复制主要应用于系统调用fork,父子进程以只读方式共享页框,当其中之一要修改页框时,内核才通过缺页异常处理程序分配一个新的页框,并将页框标记为可写。这种处理方式能够较大的提高系统的性能,这和Linux创建进程的操作过程有一定的关系。在一般情况下,子进程被创建以后会马上通过系统调用execve将一个可执行程序的映象装载进内存中,此时会重新分配子进程的页框。那么,如果fork的时候就对页框进行复制的话,显然是很不合适的。

在上述的两种情况下出现缺页异常,进程运行于用户态,异常处理程序可以让进程从出现异常的指令处恢复执行,使用户感觉不到异常的发生。当然,也会有异常无法正常恢复的情况,这时,异常处理程序会进行一些善后的工作,并结束该进程。也就是说,运行在用户态的进程如果出现缺页异常,不会对操作系统核心的稳定性造成影响。那么对于运行在核心态的进程如果发生了无法正常恢复的缺页异常,应该如何处理呢?是否会导致系统的崩溃呢?是否能够解决好内核态缺页异常对于操作系统核心的稳定性来说会产生很大的影响,如果一个误操作就会造成系统的Oops,这对于用户来说显然是不能容忍的。本文正是针对这个问题,介绍了一种Linux内核中所采取的解决方法。

在读者继续往下阅读之前,有一点需要先说明一下,本文示例中所选的代码取自于Linux-2.4.0,编译环境

Linux内核内存管理之缺页异常

在Linux内核中,进程的内存管理涉及malloc和mmap函数的使用,它们建立了进程的虚拟内存地址空间,但并未完成虚拟地址与物理地址的映射。当程序试图访问尚未建立映射关系的虚拟地址时,处理器会自动触发缺页异常。

ARM64架构将异常分为同步异常和异步异常。异步异常通常与中断相关联,而同步异常则与处理器执行时出现的错误有关。ARM异常处理的详细信息可以在相关文章中找到。

在ARM64处理器中,当发生异常时,处理器会跳转到异常向量表进行处理。以el1下的异常为例,处理器首先读取ESR(异常状态寄存器)的值来判断异常类型,并相应地跳转到不同的处理函数。data abort异常会跳转到el1_da函数,而instruction abort则会跳转到el1_ia函数。

处理函数的核心逻辑在于根据传入的ESR获取fault_info信息,进一步调用inf->fn函数进行处理。

针对触发异常的虚拟地址属于某个vma(虚拟内存区域)且具有触发页错误异常权限的情况,内核会调用handle_mm_fault函数来建立vma与物理地址之间的映射关系。具体实现逻辑通过__handle_mm_fault函数完成。

对于匿名页缺页异常,初次访问匿名映射的虚拟内存时,若未分配物理内存,首次访问会触发异常。异常处理通常涉及三种情况,其中第一种和第三种会涉及到0页,即一个全局变量,大小为一页,页对齐到bss段,初始化时被清零。

匿名页的异常处理涉及特定的函数和逻辑,如pte_mkspecial函数用于设置页表项指向0页,my_zero_pfn变量存储empty_zero_page得到的页帧号。对于首次写入匿名页的情况,通过alloc_zeroed_user_highpage_movable函数分配物理页面。读取后再次写入匿名页时,处理逻辑较为复杂,涉及对页面的复制和权限的修改。

文件页缺页异常处理通过do_swap_page函数实现,当pte内容存在但页面不在内存中时,表示页面位于swap空间。此时,通过do_swap_page函数从磁盘分配页面。

swap缓存的使用是为了优化内存和磁盘的读写性能差异。当内存充足时,Linux会利用空闲内存作为swap缓存,缓存磁盘数据以提高I/O性能。当内存紧张时,会回收这些缓存,将脏页回写到磁盘。

do_swap_page函数在内存换入时执行,查找磁盘上的slot并读取数据。换入过程包括查找磁盘上的slot、读取数据并将其复制到内存。

do_wp_page函数用于处理写时复制(copy on write)情况,其主要逻辑包括分配新的物理页、拷贝原页内容到新页,并修改页表项指向新页,并相应地修改为可写。

详解linux内核-缺页中断处理

现代处理器大部分都配备MMU,除一些小型嵌入式设备。MMU实现虚拟地址与物理地址间的转换,利用它我们能利用更多内存空间,得益于程序的局部性原理,暂时不用的数据存放磁盘。当访问到时会触发缺页中断,将所需数据从磁盘加载至内存。由此,我们得以运行程序大小超过内存容量的程序和打开超过内存容量的文件。现代处理器通过分段分页机制完成虚拟地址至物理地址的转换,一般支持二级页表或四级页表。

缺页中断处理的流程如下:

1.硬件中断内核,保存程序计数器及其他指令状态信息至特殊CPU寄存器。

2.启动汇编程序,保存通用寄存器及其他易丢失信息,防止操作系统破坏。

3.操作系统发现缺页中断,识别需要的虚拟页面。通常硬件寄存器包含信息,若无则检索程序计数器,分析当前指令,确定操作。

4.确定缺页虚拟地址,检查地址有效性与权限一致性。若不一致,向进程发送信号或终止进程。若有效且无保护错误,系统检查空闲页框。若无,则执行页面置换算法淘汰页面。

5.若选中的页框脏,则将该页写回磁盘,进行上下文切换,挂起产生缺页中断的进程,直到写入磁盘完成。写入的页框标记为忙,避免其他进程占用。

6.一旦页框干净,操作系统获取页面在磁盘的地址,通过磁盘操作将其装入。此时,产生缺页中断的进程仍挂起,如果有其他可运行的用户进程,选择该进程运行。

7.当磁盘中断发生,表示页面已加载至内存,页表更新,页框标记为正常状态。

8.恢复缺页中断前的状态,程序计数器指向中断前指令。

9.调度产生缺页中断的进程,操作系统返回调用汇编例程。

10.汇编例程恢复寄存器状态,返回用户空间,仿佛缺页中断未发生。

Linux内核对缺页异常处理复杂,基本思想与上述流程相似。首先关注struct vm_area_struct,虚拟内存区域,如.text段、数据段,每个都对应vma,在加载二进制文件时创建这些vma。

页面分类为:

匿名页:数据段、堆、栈、mmap匿名映射的页。

文件页:可执行文件代码段映射的页、普通文件映射的页。

缺页异常分类:内核态缺页异常与用户态缺页异常。内核态异常包括vmalloc区异常,vmalloc出现异常易于处理,只需同步页表即可,内核引用用户空间地址引发的异常,如用户态地址非法或页面已交换至磁盘。内核bug也是内核态缺页异常之一。内核态缺页异常频率低,因为内核态数据不会交换至磁盘。用户态缺页异常频繁,因用户态数据频繁写入交换区与文件,且进程创建时伴随着大量缺页异常。

Linux基本处理流程如下:

内核处理缺页异常的主要函数为do_page_fault。do_page_fault流程图如下。

在do_page_fault内调用的关键函数为find_vma,查找缺页地址对应的vma,vma在进程创建时分配。内核通过链表与红黑树对vma排序。

在栈增长方向向下时,若缺页地址不在任何vma闭区间内,需要扩展栈所属vma起始地址,expand_stack函数如下。

当发生正常用户空间的缺页异常,需要调用handle_mm_fault处理页表。获取缺页地址对应的页表项指针后,进入handle_pte_fault,根据条件调用不同的页框交换函数,具体代码如下。

用户态缺页页框经过以上处理,将从磁盘加载至内存,重新建立映射。

内核态缺页异常处理涉及函数,内核态分为页表未更新至最新,需要进行页表同步,调用vmalloc_fault。内核态缺页异常还有一情况,内核访问用户空间地址,若用户空间页表对应的页框已被交换至磁盘,或为错误地址,则使用fixup_exception处理。内核编译时会预留异常表空间。i386链接脚本内存布局包含以下内容:

__start__ex_table和 __stop__ex_table指定__ex_table段的起始与结束地址。*(__ex_table)将所有输入目标文件的__ex_table段组合成一个__ex_table。在内核调用copy_from_user时发生缺页异常的处理过程如下。

经过以上分析,深入理解了Linux内核的缺页异常处理。当然,还有未分析的细枝末节,但整体处理逻辑已清晰。

阅读剩余
THE END