之八:情景分析之堆栈扩展

注:本文展示的代码来自2.4.0版本的内核,入口函数do_page_fault定义在<arch/mm/fault.c>中。

总体处理流程:
 
说明:

①do_page_fault前半部分流程请参考“越界访问”的情景分析。

②虚存区间结构vm_area中包含一个vm_operations_struct类型的指针vm_ops。vm_operations_struct定义了一组函数指针,其中的nopage函数指针指定了当该区间发生了页面异常时应该执行的操作,详情请参考《虚存管理中的抽象》。作为堆栈区间的vma,跟文件系统或页面共享没有关系,因而没有指定nopage函数,内核将调用do_anonymous_page函数。

③“读操作触发的页面异常”,开始时都一律映射到同一个物理内存页面empty_zero_page上,而不管其虚拟地址是什么。empty_zero_page的定义在<include/asm/Pgtable.h>文件中

/*
 * ZERO_PAGEis a global shared page that is always zero: used
 * forzero-mapped memory areas etc..
 */
extern unsigned long empty_zero_page[1024];
#define ZERO_PAGE(vaddr)(virt_to_page(empty_zero_page))

可见这个页面的内容全为0,也就是说,在映射之初去读的话读出的值全为0。这是可以理解的:你去读一个“尚未建立映射”的地址,这个地址对用户空间来说,就像是一块新的未开垦(未进行写操作)的土地,读出的结果为0是理所应当的。如果后续要对该地址进行写操作,那么内核将会为该虚拟地址重新映射到一块新的的物理页面上。这就是内核中的COW(Copy On Write)技术,在《进程调度与切换》章节中我们还会看到。

④整个堆栈扩展的过程可以总结为:分配新的物理页面,通过设置页面表将虚拟地址映射到物理页面上。

⑤异常返回到用户空间后,将重新执行因异常而“夭折”的指令,对于该场景就是重新执行“子函数返回地址入栈”的指令,然后再继续往下执行,这点与中断不同。中断发生时,CPU会将下一条指令也就是接下来本该执行的指令的地址压入堆栈,作为中断服务的返回地址;而异常发生时,CPU将因异常无法完成的指令(也就是触发异常的指令)本身的地址压入堆栈,作为异常服务程序的返回地址,这样从异常服务返回时,就可以“从头再来”完成未竟的事业。这种差异性(返回地址的差异),是CPU内部的硬件电路实现的,不需要软件的干预。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值