【存储管理】越界访问与用户堆栈的扩展

来源:互联网 发布:java学生成绩录入 编辑:程序博客网 时间:2024/06/06 05:00

页式管理机制通过页面目录PGD,页面表PT,将每一个线性地址(虚拟地址)转换成物理地址。但是并不是每一次CPU都能访问到相应的物理内存单元,这样映射便失败了,会产生缺页异常。


(1)映射失败的原因:PGD,PT为空,映射关系尚未建立,或已撤销;相应的物理页面不在内存中;指令的访问方式与页面的访问权限不符,如写一个只读的页面。


(2)越界访问:用户空间访问到系统空间(虚拟地址大于给定的地址);mmap可将文件映射到内存中,munmap撤销映射,但是用户可能访问已撤销的区间。可获得当前进程task_struct数据结构进而得到mm_struct,通过find_vma(),找到的起始地址不高于给定的地址,说明地址在下述所示图的空洞之中(堆栈以下的供bark()动态申请的空间在vma中有vm_flags来标识VM_GROWSDOWN);如果空洞上方并不是堆栈区,那也就是映射区,error_code的bit2位表示CPU处于用户模式发生的,这时对进程的task_struct结构进行设置,发生SIGSEGV,这时程序员经常会看到异常“Segment Fault”。


(3)用户堆栈的扩展:(堆栈以下的供bark()动态申请的空间在vma中有vm_flags来标识VM_GROWSDOWN),如果用户堆栈过小,有可能就会因祸得福,一般一次压入堆栈是4个字节,但是i386有一个指令pusha,会压入32个字节,因此检查准则就是%esp-32。超出这个范围的一定不是本情况。如果属于正常堆栈扩展,我就要使用expand_stack()来扩展堆栈;expand_stack():task_struct中有个rlim的结构数组(记录各种资源的分配限制),如果扩展的大小(上限RILIMT_STACK)超过了可用于堆栈的资源,那就不扩展,返回-ENOMEM,表示没有存储空间分配了,expand_stack()返回非0(-ENOMEM),在do_page_fault()会转向bad_area,在expand_stack()中只是建立堆栈区的vm_area_struct结构,并未建立起新扩展的页面对物理内存的映射,该任务由good_area完成。


(4)用户堆栈的扩展:在good_area中,根据中断机制传过来的error_code进一步确定映射失败的原因并采取相应的措施。由于堆栈段允许写入(bit1=1,bit=0),调用虚存管理handle_mm_fault,通过pmd_alloc来分配一个中间目录项,pte_alloc为分配内存页面建立映射做好准备;handle_pte_fault()中有set_pte(),从而映射已建立好。


(5)当CPU从一次页面异常返回到用户空间时,将重新执行因映射失败而中途夭折的那条指令,继续执行。异常发生时,会将夭折的那条指令压入到堆栈中,由于堆栈已经扩展了,这条指令就可以重新执行了,这一过程都是透明的,由Linux做好的。





0 0
原创粉丝点击