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

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

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

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

[cpp] view plaincopy
  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(),这个函数在上一篇博文中分析了其对内核的非法访问的处理,现在看其对用户空间的非法访问的处理

[cpp] view plaincopy
  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()函数来完成

[cpp] view plaincopy
  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用户空间 限制吗,在Linux中限制用户空间

    在Linux中限制用户的磁盘使用空间使用的是Quota,一般Quota在安装Linux时就已包括在内核中,不需要另行安装.Quota可以从两方面指定磁盘的储存限制: 使用者所能够支配的索引节点(ino ...

  2. Openwrt按键检测分析-窥探Linux内核与用户空间通讯机制netlink使用

    首先看一下Openwrt系统中关于按键功能的使用和修改,以18.06版本为例 按键功能实现在脚本中, 比如18.06/package/base-files/files/etc/rc.button/re ...

  3. linux内核与用户空间的九种通信机制

    目前Linux提供了9种机制完成内核与用户空间的数据交换,分别是内核启动参数.模块参数与 sysfs.sysctl.系统调用.netlink.procfs.seq_file.debugfs和relay ...

  4. 人间还是仙界?聊一聊linux系统的用户空间和内核空间

    我们生活在人间,但<西游记>里提到,在天上还有一个仙界. 人间不知道仙界的存在:而仙界知道人间的存在,神仙也可以从仙界下凡到人间,但是被严格管控的. 软件设计的灵感其实都来自于生活:用户空 ...

  5. 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. 参考代码 ...

  6. linux内核那些事之用户空间管理

    内核主要数据结构 linux内核将用户空间抽象成struct vm_area_struct进行管理,每申请以个用户空间在内核中都会抽象成对应的vm_are_struct进行管理,同时为了区别不同进程的 ...

  7. linux 用户空间文件系统 filesystem in userspace fuse 简介

    用户空间文件系统(Filesystem in Userspace,简称FUSE)是操作系统中的概念,指完全在用户态实现的文件系统.目前Linux通过内核模块对此进行支持.一些文件系统如ZFS,glus ...

  8. linux 内核空间 sy,在 Linux 下用户空间与内核空间数据交换的方式,第 1 部分: 内核启动参数、模块参数与sysf...

    级别: 初级 燚 杨 (), 计算机科学硕士 2006 年 2 月 16 日 本系列文章包括两篇,它们文详细地介绍了 Linux 系统下用户空间与内核空间数据交换的九种方式,包括内核启动参数.模块参数 ...

  9. FUSE——用户空间文件系统

    用户空间文件系统(Filesystem in Userspace,简称FUSE)是操作系统中的概念,指完全在用户态实现的文件系统. 目前Linux通过内核模块对此进行支持.一些文件系统如ZFS,glu ...

  10. 一种内核到用户空间的高效数据传输技术

    级别: 中级 桂 剑 (guijian@cn.ibm.com), IBM 中国开发中心,Linux Performance 项目软件工程师 2006 年 12 月 28 日 Relay 是一种从 Li ...

最新文章

  1. 计算机视觉:你必须了解的图像数据底层技术
  2. 【Flutter】Animation 动画 ( AnimatedWidget 动画使用流程 | 创建动画控制器 | 创建动画 | 创建 AnimatedWidget 动画组件 | 动画运行 )
  3. CSS浏览器兼容性----Hack
  4. Opencv 深度学习识别性别和检测年龄
  5. Java8学习笔记(七)--Collectors
  6. 字符串匹配KMP算法设计C语言,KMP字符串匹配算法笔记
  7. OkHttp3源码详解(五) okhttp连接池复用机制
  8. 青少年编程python等级考试题目_2020年全国青少年软件编程(python)等级考试试卷doc下载...
  9. SAP+条码系统软件是如何优化仓库库存管理?
  10. 关于WireShark跟随数据流后entire conversation显示的字节数分析
  11. 一种很强的对联,看了让我想起高中时期那会儿对中文的崇拜!
  12. 【unity地编】unity制作场景的流程和要点简要
  13. Unirech腾讯云国际代充-云服务器cvm常见问题解答
  14. 51矩阵键盘数码管动态显示
  15. 筛法(线性筛,厄拉多塞筛)
  16. echarts.js(echarts.min.js)的内容
  17. Google Earth Engine(GEE)——在线计算列表二维ee.List对象为线性回归方程计算slope和残差
  18. kafka 生产者源码解析
  19. 基于iBeacon技术智慧社区医院解决方案
  20. 精品连载丨安卓 App 逆向课程之二逆向神器 frida 的介绍

热门文章

  1. 实现MFC扩展DLL中导出类和对话框
  2. [导入]如何在标题栏上增加按钮
  3. java代码调用python_java 直接调用python脚本,并传递参数代码实例
  4. JetBrains注册码计算(IntelliJ IDEA 15.0注册码激活)
  5. chaos-monkey-spring-boot小试牛刀
  6. MapReduce实现WordCount
  7. arduino 部分有用的函数
  8. Linux驱动中相关函数查询
  9. CAP以及分区容错性的含义
  10. application context not configured for this file于spring框架使用中的原因