转自: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. 37-生成 JWT Token

    接到上篇文章 安装扩展插件nuget package方法安装包 使用 ctrl+shift+p打开命令面板 增加这个包,  Microsoft.AspNetCore.Authentication.Jw ...

  2. hdu1251统计难题(trie)

    统计难题 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131070/65535 K (Java/Others)Total Submi ...

  3. sql server inser相关处理(添加一条,一次添加多条,Bulk插入多条)

    1,insert语句 insert into 表一(字段一,字段二,字段三) value(值一,值二,值三) 2,sql 插入多条语句,其中完整值之间用逗号分割 insert into 表一(字段一, ...

  4. jQuery的Ajax初识

    1. 什么是Ajax? Ajax是“Asynchronous Javascript And XML(异步Javascript和XML)”的缩写, 是指一种创建交互式网页应用的网站开发技术. Ajax不 ...

  5. The Django Book

    The Django Book Table of contents 2.0, English -> Chinese Django book 2.0 的中文翻译. 最近更新 - 贡献者 方便自己也 ...

  6. 用Jenkins自动化搭建测试环境

    1-1 课程介绍 2-1 Jenkins安装 2-2 Jenkins插件 2-3 Jenkins基础设置 3-1 Linux系统准备 3-2 安装Java环境 3-3 安装并配置Git 3-4 安装并 ...

  7. JavaWeb项目中使用ajax上传文件

    1.jsp $("#cxsc").click(function(){ var bankId = $("#bankId").val(); var formdata ...

  8. ctags+cscope替换sourceinsight

    背景 windows环境开发+linux交叉编译的开发模式,代码阅读和编写都用的source-insight. 除了检索,跳转,工程构建等方面,sourceinsight自带了一些宏语言,可以轻松实现 ...

  9. iOS CGContextRef 画一条直线,仅仅是画一条直线

    今天周末休息,想好好补补课,无奈,弄了一上午,全部都是半边拉块的demo,有一种深深的挫败感. 中午睡醒一觉后,又看了一集“奔跑吧兄弟”,然后一下午时间就过去了. 仔细一想,应该是我的补课方法不对:要 ...

  10. Servlet 返回Json数据格式

    其实就是把数据库中的数据查询出来拼接成一个Json数据 import dao.UserDao; import endy.User; import javax.servlet.ServletExcept ...