转自:http://blog.csdn.net/vanbreaker/article/details/7870769

版权声明:本文为博主原创文章,未经博主允许不得转载。

用户空间的缺页异常可以分为两种情况--

1.触发异常的线性地址处于用户空间的vma中,但还未分配物理页,如果访问权限OK的话内核就给进程分配相应的物理页了

2.触发异常的线性地址不处于用户空间的vma中,这种情况得判断是不是因为用户进程的栈空间消耗完而触发的缺页异常,如果是的话则在用户空间对栈区域进行扩展,并且分配相应的物理页,如果不是则作为一次非法地址访问来处理,内核将终结进程

下面来看do_page_fault()函数对用户空间缺页异常的处理

  1. dotraplinkage void __kprobes
  2. do_page_fault(struct pt_regs *regs, unsigned long error_code)
  3. {
  4. struct vm_area_struct *vma;
  5. struct task_struct *tsk;
  6. unsigned long address;
  7. struct mm_struct *mm;
  8. int write;
  9. int fault;
  10. tsk = current; //获取当前进程
  11. mm = tsk->mm;  //获取当前进程的地址空间
  12. /* Get the faulting address: */
  13. address = read_cr2(); //读取CR2寄存器获取触发异常的访问地址
  14. ...
  15. ...
  16. ...
  17. ...
  18. vma = find_vma(mm, address);//试图寻找到一个离address最近的vma,vma包含address或在address之后
  19. /*没有找到这样的vma则说明address之后没有虚拟内存区域,因此该address肯定是无效的,
  20. 通过bad_area()路径来处理,bad_area()的主体就是__bad_area()-->bad_area_nosemaphore()*/
  21. if (unlikely(!vma)) {
  22. bad_area(regs, error_code, address);
  23. return;
  24. }
  25. /*如果该地址包含在vma之中,则跳转到good_area处进行处理*/
  26. if (likely(vma->vm_start <= address))
  27. goto good_area;
  28. /*不是前面两种情况的话,则判断是不是由于用户堆栈所占的页框已经使用完,而一个PUSH指令
  29. 引用了一个尚未和页框绑定的虚拟内存区域导致的一个异常,属于堆栈的虚拟内存区,其VM_GROWSDOWN位
  30. 被置位*/
  31. if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) {
  32. bad_area(regs, error_code, address);//不是堆栈区域,则用bad_area()来处理
  33. return;
  34. }
  35. if (error_code & PF_USER) {//必须处于用户空间
  36. /*
  37. * Accessing the stack below %sp is always a bug.
  38. * The large cushion allows instructions like enter
  39. * and pusha to work. ("enter $65535, $31" pushes
  40. * 32 pointers and then decrements %sp by 65535.)
  41. */
  42. /*这里检查address,只有该地址足够高(和堆栈指针的差不大于65536+32*sizeof(unsigned long)),
  43. 才能允许用户进程扩展它的堆栈地址空间,否则bad_area()处理*/
  44. if (unlikely(address + 65536 + 32 * sizeof(unsigned long) < regs->sp)) {
  45. bad_area(regs, error_code, address);
  46. return;
  47. }
  48. }
  49. if (unlikely(expand_stack(vma, address))) {//堆栈扩展不成功同样由bad_area()处理
  50. bad_area(regs, error_code, address);
  51. return;
  52. }
  53. /*
  54. * Ok, we have a good vm_area for this memory access, so
  55. * we can handle it..
  56. */
  57. good_area:
  58. write = error_code & PF_WRITE;
  59. /*访问权限不够则通过bad_area_access_error()处理,该函数是对__bad_area()的封装,只不过
  60. 发送给用户进程的信号为SEGV_ACCERR*/
  61. if (unlikely(access_error(error_code, write, vma))) {
  62. bad_area_access_error(regs, error_code, address);
  63. return;
  64. }
  65. /*
  66. * If for any reason at all we couldn't handle the fault,
  67. * make sure we exit gracefully rather than endlessly redo
  68. * the fault:
  69. */
  70. /*分配新的页表和页框*/
  71. fault = handle_mm_fault(mm, vma, address, write ? FAULT_FLAG_WRITE : 0);
  72. if (unlikely(fault & VM_FAULT_ERROR)) {
  73. mm_fault_error(regs, error_code, address, fault);
  74. return;
  75. }
  76. if (fault & VM_FAULT_MAJOR) {
  77. tsk->maj_flt++;
  78. perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0,
  79. regs, address);
  80. } else {
  81. tsk->min_flt++;
  82. perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0,
  83. regs, address);
  84. }
  85. check_v8086_mode(regs, address, tsk);
  86. up_read(&mm->mmap_sem);
  87. }

bad_area()函数的主体函数为__bad_area()-->__bad_area_nosemaphore(),这个函数在上一篇博文中分析了其对内核的非法访问的处理,现在看其对用户空间的非法访问的处理

  1. __bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code,
  2. unsigned long address, int si_code)
  3. {
  4. struct task_struct *tsk = current;
  5. /* User mode accesses just cause a SIGSEGV */
  6. /*错误发生在用户态,则向用户进程发送一个SIGSEG信号V*/
  7. if (error_code & PF_USER) {
  8. /*
  9. * It's possible to have interrupts off here:
  10. */
  11. local_irq_enable();
  12. /*
  13. * Valid to do another page fault here because this one came
  14. * from user space:
  15. */
  16. if (is_prefetch(regs, error_code, address))
  17. return;
  18. if (is_errata100(regs, address))
  19. return;
  20. if (unlikely(show_unhandled_signals))
  21. show_signal_msg(regs, error_code, address, tsk);
  22. /* Kernel addresses are always protection faults: */
  23. tsk->thread.cr2      = address;
  24. tsk->thread.error_code   = error_code | (address >= TASK_SIZE);
  25. tsk->thread.trap_no  = 14;
  26. force_sig_info_fault(SIGSEGV, si_code, address, tsk);
  27. return;
  28. }
  29. ...
  30. ...
  31. }

在确定了这次异常是因为物理页没分配而导致后,就通过good_area路径来处理,可想而知,该路径在确定了访问权限足够后,将完成页表和物理页的分配,这个任务有handle_mm_fault()函数来完成

  1. int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
  2. unsigned long address, unsigned int flags)
  3. {
  4. pgd_t *pgd;
  5. pud_t *pud;
  6. pmd_t *pmd;
  7. pte_t *pte;
  8. __set_current_state(TASK_RUNNING);
  9. count_vm_event(PGFAULT);
  10. if (unlikely(is_vm_hugetlb_page(vma)))
  11. return hugetlb_fault(mm, vma, address, flags);
  12. pgd = pgd_offset(mm, address);
  13. pud = pud_alloc(mm, pgd, address);//分配pud目录
  14. if (!pud)
  15. return VM_FAULT_OOM;
  16. pmd = pmd_alloc(mm, pud, address);//分配pmd目录
  17. if (!pmd)
  18. return VM_FAULT_OOM;
  19. pte = pte_alloc_map(mm, pmd, address);//分配pte表
  20. if (!pte)
  21. return VM_FAULT_OOM;
  22. /*handle_pte_fault()的任务就是为pte绑定新的页框,它会根据pte页表项的情况来做不同的处理*/
  23. return handle_pte_fault(mm, vma, address, pte, pmd, flags);
  24. }

handle_pte_fault()函数的处理比较复杂,因为它要根据pte页表项对应的物理页的不同状态来做各种不同的处理,具体的分析以后再给出。

linux缺页异常处理--用户空间【转】的更多相关文章

  1. linux缺页异常处理--内核空间

    缺页异常被触发通常有两种情况-- 程序设计的不当导致访问了非法的地址 访问的地址是合法的,但是该地址还未分配物理页框. 下面解释一下第二种情况,这是虚拟内存管理的一个特性.尽管每个进程独立拥有3GB的 ...

  2. linux缺页异常处理--内核空间【转】

    转自:http://blog.csdn.net/vanbreaker/article/details/7867720 版权声明:本文为博主原创文章,未经博主允许不得转载. 缺页异常被触发通常有两种情况 ...

  3. Linux环境下用户空间与内核空间数据的交换方式

    在linux环境开发过程中,经常会需要在用户空间和内核空间之间进行数据交换. 介绍了 Linux 系统下用户空间与内核空间数据交换的几种方式 第一节:使用procfs实现内核交互简明教程(1) 第二节 ...

  4. Linux内核访问用户空间文件:get_fs()/set_fs()的使用

    测试环境:Ubuntu 14.04+Kernel 4.4.0-31 关键词:KERNEL_DS.USER_DS.get_fs().set_fs().addr_limit.access_ok. 参考代码 ...

  5. Linux调度器 - 用户空间接口

    一.前言 Linux调度器神秘而充满诱惑,每个Linux工程师都想深入其内部一探究竟.不过中国有一句古话叫做“相由心生”,一个模块精巧的内部逻辑(也就是所谓的“心”)其外延就是简洁而优雅的接口(我称之 ...

  6. linux下在用户空间访问I/O端口的ioperm和iopl函数

    1.ioperm函数      功能描述:为调用进程设置I/O端口访问权能.ioperm的使用需要具有超级用户的权限,只有低端的[0-0x3ff] I/O端口可被设置,要想指定更多端口的权能,可使用i ...

  7. Linux内存管理--用户空间和内核空间【转】

    本文转载自:http://blog.csdn.net/yusiguyuan/article/details/12045255 关于虚拟内存有三点需要注意: 4G的进程地址空间被人为的分为两个部分--用 ...

  8. linux内存管理--用户空间和内核空间

    关于虚拟内存有三点需要注意: 4G的进程地址空间被人为的分为两个部分--用户空间与内核空间.用户空间从0到3G(0xc0000000),内核空间占据3G到4G.用户进程通常情况下只能访问用户空间的虚拟 ...

  9. [转]五个Linux下用户空间的调试工具

    有几个Linux下的用户空间调试工具和技术,它们用来分析用户空间的问题相当有用.它们是: 'print' 语句 查询 (/proc, /sys 等) 跟踪 (strace/ltrace) Valgri ...

随机推荐

  1. LoadRunner使用代理远程执行提示找不到“pre_cci.c”文件

    好久没有使用LoadRunner了,工作需要使用一下,执行总是提示找不到“pre_cci.c”文件,找问题花了很长时间终于找到问题了.万事还是需要找到原因: cci 会将 pre_cci.c 文件作为 ...

  2. 十四、pymysql模块

    一.安装的两种方法 第一种 #安装 pip3 install pymysql 第二种 二.链接,执行sql,关闭(游标) import pymysql user= input('用户名:>> ...

  3. 6,MongoDB 之 Array Object 的特殊操作

    相比关系型数据库, Array [1,2,3,4,5] 和 Object { 'name':'DragonFire' } 是MongoDB 比较特殊的类型了 特殊在哪里呢?在他们的操作上又有什么需要注 ...

  4. border与background定位

    1.background定位的局限 只能相对于左上角数值定位,不能相对于右下 即background-position默认相对于左上方定位的 2.怎样让图片相对于右下角? background-pos ...

  5. DOS程序员手册(七)

    第11章      中断处理程序 本章将深入到DOS系统内部探讨中断处理程序的内容.与其他计算机编程不一样, 中断处理程序这个名词听起来就很难懂.用最简单的话来说,中断处理程序就是对应于中 断激活的程 ...

  6. 使用node构建一个自己的服务器

    我们做本地服务器,经常会选择Apache.IIS或者Tomcat,当然这些最方便的算是Apache,几乎不需要配置,最多就是配置下端口,亦或者我们想不用localhost,改成其他也是可以的,只要去更 ...

  7. 使用 JsonPath 完成接口自动化测试中参数关联和数据验证(Python语言)

    背景: 接口自动化测试实现简单.成本较低.收益较高,越来越受到企业重视 restful风格的api设计大行其道 json成为主流的轻量级数据交换格式 痛点: 接口关联 也称为关联参数.在应用业务接口中 ...

  8. ASP.NET Core ---异常处理

    一.局部异常处理: 在Action里面catch 二.全局异常处理: 1.默认的异常处理配置: 默认配置在StartUp文件的Configure中注册错误处理,显示开发者错误页面: public vo ...

  9. SpringMVC<一> 基本结构与配置

    刚刚踏入SpringMVC的学习,有一定Strust2的使用经验,边看书看博客,边总结,如有不对的地方还希望各位大佬多多指正. Spring 响应过程与结构 (1)用户在客户端发送一个HTTP请求,W ...

  10. 一些需要注意的ts

    写了一段时间ts,在从头学习一遍,温故而之新 ts的一些技巧 1.巧用注释 通过/** */形式的注释可以给 TS 类型做标记,编辑器会有更好的提示: /** A cool guy. */ inter ...