linux缺页异常处理--用户空间【转】

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

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

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

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

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

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

[cpp] view plain copy

  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 plain copy

  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 plain copy

  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页表项对应的物理页的不同状态来做各种不同的处理,具体的分析以后再给出。

时间: 2024-10-07 15:41:51

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

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

http://blog.csdn.net/vanbreaker/article/details/7867720 缺页异常被触发通常有两种情况-- 1.程序设计的不当导致访问了非法的地址 2.访问的地址是合法的,但是该地址还未分配物理页框 下面解释一下第二种情况,这是虚拟内存管理的一个特性.尽管每个进程独立拥有3GB的可访问地址空间,但是这些资源都是内核开出的空头支票,也就是说进程手握着和自己相关的一个个虚拟内存区域(vma),但是这些虚拟内存区域并不会在创建的时候就和物理页框挂钩,由于程序的局部

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

转自:http://blog.csdn.net/vanbreaker/article/details/7867720 版权声明:本文为博主原创文章,未经博主允许不得转载. 缺页异常被触发通常有两种情况—— 1.程序设计的不当导致访问了非法的地址 2.访问的地址是合法的,但是该地址还未分配物理页框 下面解释一下第二种情况,这是虚拟内存管理的一个特性.尽管每个进程独立拥有3GB的可访问地址空间,但是这些资源都是内核开出的空头支票,也就是说进程手握着和自己相关的一个个虚拟内存区域(vma),但是这些虚

ubuntu+systemtap进行Linux内核和用户空间开发测试

ubuntu+systemtap进行Linux内核和用户空间开发测试 Sailor_forever  sailing_9806#163.com (本原创文章发表于Sailor_forever 的个人blog,未经本人许可,不得用于商业用途.任何个人.媒体.其他网站不得私自抄袭:网络媒体转载请注明出处,增加原文链接,否则属于侵权行为.如有任何问题,请留言或者发邮件给sailing_9806#163.com) [摘要]本文主要介绍在ubuntu平台 + 自定义内核上如何安装systemtap工具包及

Linux调度器 - 用户空间接口

一.前言 Linux调度器神秘而充满诱惑,每个Linux工程师都想深入其内部一探究竟.不过中国有一句古话叫做"相由心生",一个模块精巧的内部逻辑(也就是所谓的"心")其外延就是简洁而优雅的接口(我称之为"相").通过外部接口的定义,其实我们也可以收获百分之六七十的该模块的内部信息.因此,本文主要描述Linux调度器开放给用户空间的接口,希望可以通过用户空间的调度器接口来理解Linux调度器的行为. 二.nice函数 nice函数用来修改调用进程的

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

本文转载自:http://blog.csdn.net/yusiguyuan/article/details/12045255 关于虚拟内存有三点需要注意: 4G的进程地址空间被人为的分为两个部分--用户空间与内核空间.用户空间从0到3G(0xc0000000),内核空间占据3G到4G.用户进程通常情况下只能访问用户空间的虚拟地址,不能访问内核空间的虚拟地址.例外情况只有用户进程进行系统调用(代表用户进程在内核态执行)等时刻可以访问到内核空间. 用户空间对应进程,所以每当进程切换,用户空间就会跟着

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

有几个Linux下的用户空间调试工具和技术,它们用来分析用户空间的问题相当有用.它们是: 'print' 语句 查询 (/proc, /sys 等) 跟踪 (strace/ltrace) Valgrind (memwatch) GDB 让我们一个个地了解. 1.'print' 语句 这是一个基本的原始的调试问题的方法. 我们可以在程序中插入print语句来了解控制流和变量值. 虽然这是一个简单的技术, 但它有一些缺点. 程序需要进行编辑以添加'print'语句,然后必须重新编译,重新运行来获得输

用户空间缺页异常pte_handle_fault()分析--(上)【转】

转自:http://blog.csdn.net/vanbreaker/article/details/7881206 版权声明:本文为博主原创文章,未经博主允许不得转载. 前面简单的分析了内核处理用户空间缺页异常的流程,进入到了handle_mm_fault()函数,该函数为触发缺页异常的地址address分配各级的页目录,也就是说现在已经拥有了一个和address配对的pte了,但是这个pte如何去映射物理页框,内核又得根据pte的状态进行分类和判断,而这个过程又会牵扯出一些其他的概念……这也

linux用户空间和内核空间

When a process running in user mode requests additional memory, pages are allocated from the list of free page frames maintained by the kernel. This list is typically populated using a page-replacement algorithm such as those discussed in Section 9.4

Linux Malloc分析-从用户空间到内核空间

本文介绍malloc的实现及其malloc在进行堆扩展操作,并分析了虚拟地址到物理地址是如何实现映射关系. ordeder原创,原文链接: http://blog.csdn.net/ordeder/article/details/41654509 1背景知识 1.1 进程的用户空间 图1:来源 http://www.open-open.com/lib/view/open1409716051963.html 该结构是由进程task_struct.mm_struct进行管理的mm_struct的定义