首页 | Linux 基础 | 资讯动态 | Linux 应用 | Linux 服务器 | Linux 开发 | Linux 安全 | 专题 | 联盟论坛
  当前位置:主页>Linux 开发>linux 内核>文章内容
借助异常表处理Linux内核态缺页异常情况
来源:http://www.unix5.com 作者:riechelr_hl 发布时间:2007-07-12  
extern inline int verify_area(int type, const void * addr, unsigned long size)

  

该函数验证了是否可以以type中说明的访问类型(read or write)访问从地址addr开始、大小为size的一块虚拟存储区域。为了做到这一点,verify_read首先需要找到包含地址addr的虚拟存储区域(vma)。一般的情况下(正确运行的程序)这个测试都会成功返回,在少数情况下才会出现失败的情况。也就是说,大部分的情况下内核在一些无用的验证操作上花费了不算短的时间,这从操作系统运行效率的角度来说是不可接受的。

为了解决这个问题,现在的Linux设计中把验证的工作交给虚存中的硬件设备来完成。当系统启动分页机制以后,如果一条指令的虚拟地址所对应的页框(page frame)不在内存中或者访问的类型有错误,就会发生缺页异常。处理器把引起缺页异常的虚拟地址装到寄存器CR2中,并提供一个出错码,指示引起缺页异常的存储器访问的类型,随后调用Linux的缺页异常处理函数进行处理。

Linux中进行缺页异常处理的函数如下:

 

 asmlinkage void do_page_fault
(struct pt_regs *regs, unsigned long error_code)
  {
  ……………………
   __asm__("movl %%cr2,%0":"=r" (address));
   ……………………
   vma = find_vma(mm, address);
   if (!vma)
   goto bad_area;
   if (vma->vm_start <= address)
   goto good_area;
   if (!(vma->vm_flags & VM_GROWSDOWN))
   goto bad_area;
   if (error_code & 4) {
   if (address + 32 < regs->esp)
   goto bad_area;
   ……………………
  bad_area:
   ……………………
  no_context:
   /* Are we prepared to handle this kernel fault? */
   if ((fixup = search_exception_table(regs->eip)) != 0) {
   regs->eip = fixup;
   return;
   }
   ………………………
  }

首先让我们来看看传给这个函数调用的两个参数:它们都是通过entry.S在堆栈中建立的(arch/i386/kernel/entry.S),参数regs指向保存在堆栈中的寄存器,error_code中存放着异常的出错码,具体的堆栈布局参见图一(堆栈的生成过程请参考《Linux内核源代码情景分析》一书)

该函数首先从CPU的控制寄存器CR2中获取出现缺页异常的虚拟地址。由于缺页异常处理程序需要处理的缺页异常类型很多,分支也很复杂。基于本文的主旨,我们只关心以下的几种内核缺页异常处理的情况:

1." 程序要访问的内核地址空间的内容不在内存中,先跳转到标号vmalloc_fault,如果当前访问的内容所对应的页目录项不在内存中,再跳转到标号no_context;

2. 缺页异常发生在中断或者内核线程中,跳转到标号no_context;

3. 程序在核心态运行时访问用户空间的数据,被访问的数据不在内存中

a) 出现异常的虚拟地址在进程的某个vma中,但是系统内存无法分配空闲页框(page frame),则先跳转到标号out_of_memory,再跳转到标号no_context;

b) 出现异常的虚拟地址不属于进程任一个vma,而且不属于堆栈扩展的范畴,则先跳转到标号bad_area,最终也是到达标号no_context。

从上面的这几种情况来看,我们关注的焦点最后集中到标号no_context处,即对函数search_exception_table的调用。这个函数的作用就是通过发生缺页异常的指令(regs->eip)在异常表(exception table)中寻找下一条可以继续运行的指令(fixup)。这里提到的异常表包含一些地址对,地址对中的前一个地址表示出现异常的指令的地址,后一个表示当前一个指令出现错误时,程序可以继续得以执行的修复地址。

如果这个查找操作成功的话,缺页异常处理程序把堆栈中的返回地址(regs->eip)修改成修复地址并返回,随后,发生异常的进程把按照fixup中安排好的指令继续执行下去。当然,如果无法找到与之匹配的修复地址,系统只有打印出出错信息并停止运作。

那么,这个所谓的修复地址又是怎么样生成的呢?是系统自动生成的吗?答案当然是否定的,这些修复指令都是编程人员通过as提供的扩展功能写进内核源码中的。下面我们就来分析一下其实现机制。

异常表的实现机制

笔者取include/asm-i386/uaccess.h中的宏定义__copy_user编写了一段程序作为例子加以讲解。

 


共6页: 上一页 [1] 2 [3] [4] [5] [6] 下一页
 
如果您对本文有任何疑问或者建议,请到论坛讨论区发表您的意见: >> 论坛入口
[收藏] [推荐] [评论(0条)] [返回顶部] [打印本页] [关闭窗口]  
  热点文章
·使用 Linux 系统调用的内核命令
·Linux 2.6.11内核文件IO系统调用
·Linux操作系统的源代码目录树结
·Linux用户态与内核态的交互讲解
·Linux内核对I/O端口的管理实现(
·深入分析 Linux操作系统的内核链
·Linux内核可装载模块对设备驱动
·概述Linux系统的驱动框架及驱动
·详解Linux 2.6内核新文件系统变
·Linux系统可卸载内核模块完全指
·FreeBSD手册讲解(一)--配置FreeB
·编译Linux操作系统的内核讲解
  相关文章
·编译Linux操作系统的内核讲解
·关于Linux内核版本稳定性能测试
·Linux操作系统的内核模块全面解
· Linux操作系统内核指导——虚拟
·Linux系统内核研究之可执行文件
·Linux系统内核网络参数意义以及
·SYN Cookie原理以及在Linux系统
·Linux操作系统动态函式库讲解(
·Linux操作系统动态函式库讲解(二
·Linux系统内核:修改TCP/IP调优参
·Linux内核空间保护与空间数据传
·编译支持NTFS的Linux系统内核模

本站信息源至:互联网络,均为学习,交流所用,如有版权问题,请联系我们.
站长QQ:397422079 E_mail:riechelr_hl@unix5.com
转载本站内容请注明原作者名.谢谢!