Linux缺页异常处理--内核空间
Linux缺页异常处理--内核空间
缺页异常被触发通常有两种情况——
1.程序设计的不当导致访问了非法的地址
2.访问的地址是合法的,但是该地址还未分配物理页框
下面解释一下第二种情况,这是虚拟内存管理的一个特性。尽管每个进程独立拥有3GB的可访问地址空间,但是这些资源都是内核开出的空头支票,也就是说进程手握着和自己相关的一个个虚拟内存区域(vma),但是这些虚拟内存区域并不会在创建的时候就和物理页框挂钩,由于程序的局部性原理,程序在一定时间内所访问的内存往往是有限的,因此内核只会在进程确确实实需要访问物理内存时才会将相应的虚拟内存区域与物理内存进行关联(为相应的地址分配页表项,并将页表项映射到物理内存),也就是说这种缺页异常是正常的,而第一种缺页异常是不正常的,内核要采取各种可行的手段将这种异常带来的破坏减到最小。
缺页异常的处理函数为do_page_fault(),该函数是和体系结构相关的一个函数,缺页异常的来源可分为两种,一种是内核空间(访问了线性地址空间的第4个GB),一种是用户空间(访问了线性地址空间的0~3GB),以X86架构为例,先来看内核空间异常的处理。
- dotraplinkage void __kprobes
- do_page_fault(struct pt_regs *regs, unsigned long error_code)
- {
- struct vm_area_struct *vma;
- struct task_struct *tsk;
- unsigned long address;
- struct mm_struct *mm;
- int write;
- int fault;
- tsk = current; //获取当前进程
- mm = tsk->mm; //获取当前进程的地址空间
- /* Get the faulting address: */
- address = read_cr2(); //读取CR2寄存器获取触发异常的访问地址
- ...
- ...
- if (unlikely(fault_in_kernel_space(address))) { //判断address是否处于内核线性地址空间
- if (!(error_code & (PF_RSVD | PF_USER | PF_PROT))) {//判断是否处于内核态
- if (vmalloc_fault(address) >= 0)//处理vmalloc异常
- return;
- if (kmemcheck_fault(regs, address, error_code))
- return;
- }
- /* Can handle a stale RO->RW TLB: */
- /*异常发生在内核地址空间但不属于上面的情况或上面的方式无法修正,
- 则检查相应的页表项是否存在,权限是否足够*/
- if (spurious_fault(error_code, address))
- return;
- /* kprobes don't want to hook the spurious faults: */
- if (notify_page_fault(regs))
- return;
- /*
- * Don't take the mm semaphore here. If we fixup a prefetch
- * fault we could otherwise deadlock:
- */
- bad_area_nosemaphore(regs, error_code, address);
- return;
- }
- ...
- ...
- }
|
评论暂时关闭