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

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

 

在确定了这次异常是因为物理页没分配而导致后,就通过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.   
  9.     __set_current_state(TASK_RUNNING);  
  10.   
  11.     count_vm_event(PGFAULT);  
  12.   
  13.     if (unlikely(is_vm_hugetlb_page(vma)))  
  14.         return hugetlb_fault(mm, vma, address, flags);  
  15.   
  16.     pgd = pgd_offset(mm, address);  
  17.     pud = pud_alloc(mm, pgd, address);//分配pud目录  
  18.     if (!pud)  
  19.         return VM_FAULT_OOM;  
  20.     pmd = pmd_alloc(mm, pud, address);//分配pmd目录  
  21.     if (!pmd)  
  22.         return VM_FAULT_OOM;  
  23.     pte = pte_alloc_map(mm, pmd, address);//分配pte表  
  24.     if (!pte)  
  25.         return VM_FAULT_OOM;  
  26.   
  27.     /*handle_pte_fault()的任务就是为pte绑定新的页框,它会根据pte页表项的情况来做不同的处理*/  
  28.     return handle_pte_fault(mm, vma, address, pte, pmd, flags);  
  29. }  

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

时间: 2024-10-23 18:25:45

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

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

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

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

关于虚拟内存有三点需要注意: 4G的进程地址空间被人为的分为两个部分--用户空间与内核空间.用户空间从0到3G(0xc0000000),内核空间占据3G到4G.用户进程通常情况下只能访问用户空间的虚拟地址,不能访问内核空间的虚拟地址.例外情况只有用户进程进行系统调用(代表用户进程在内核态执行)等时刻可以访问到内核空间. 用户空间对应进程,所以每当进程切换,用户空间就会跟着变化:而内核空间是由内核负责映射,它并不会跟着进程变化,是固定的.内核空间地址有自己对应的页表,用户进程各自有不同的页表. 每

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

五个 Linux 下用户空间的调试工具 根据定义,调试工具是那些那些使我们能够监测.控制和纠正其他程序的程序.我们为什么应该用调试工具呢? 在有些情况下,运行一些程序的时候我们会被卡住,我们需要明白究竟发生了什么. 例如,我们正在运行应用程序,它产生了一些错误消息.要修复这些错误,我们应该先找出为什么产生这些错误的消息和这些错误消息从哪里产生的. 一个应用程序可能突然挂起,我们必须了解其他什么进程同时在运行.我们可能还必须弄清楚某个进程挂起的时候在做什么.为了剖析这些细节, 我们需要调试工具的帮

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

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

Linux架构简介:内核空间,用户空间和虚拟文件系统

首先有两个概念,内核空间和用户空间: 内核空间是指当kernel 执行时会占据一段系统的内存空间,这段空间就是内核空间,用户是无法和内核空间直接交互的.操作系统最主要的核心就是在这段空间里面运行. 内核空间的地址分配可以在 /proc/iomem文件中看到: 用户空间是指当用户执行程序时所占用的内存部分,这些用户程序无法直接使用内核空间的资源,必须通过虚拟文件系统才可以使用内核的资源. 所以,虚拟文件系统就是用来让用户和内核交互用的,它分为以下部分: 设备文件目录 /dev 程序信息与系统设置目

linux内核空间和用户空间的是怎样区别的,如何交互,如何从用户空间进入内核空间

转载 - linux内核空间和用户空间的是怎样区别的,如何交互,如何从用户空间进入内核空间 作者 digoal 日期 2016-11-20 标签 Linux , 用户空间 , 内核空间 , 用户态 , 内核态 , 进程上下文 , 中断上下文 背景 原文 http://blog.csdn.net/bingqingsuimeng/article/details/7924756 原文 linux驱动程序一般工作在内核空间,但也可以工作在用户空间.下面我们将详细解析,什么是内核空间,什么是用户空间,以及

如何看待Linux操作系统的用户空间和内核空间

作为中央核心处理单元的CPU,除了生产工艺的不断革新进步外,在处理数据和响应速度方面也需要有权衡.稍有微机原理基础的人都知道Intel X86体系的CPU提供了四种特权模式ring0~ring3,其中ring0特权最高,ring3的特权最低,之所以要做这样的区分一个主要目的是保护资源,通俗来讲要保护的资源无非就是"内存.I/O端口以及执行特殊机器指令的能力".任何一个时刻,x86 CPU都是在一定的特权模式下运行.同样,对于ARM体系的CPU 一共有七种运行模式,分别是:用户模式(us

如何在Linux用户空间做我们会在内核空间做的事情

我相信,Linux 最好也是最坏的事情,就是内核空间(kernel space)和用户空间(user space)之间的巨大差别. 如果没有这个区别,Linux 可能也不会成为世界上影响力最大的操作系统.如今,Linux 的使用范围在世界上是最大的,而这些应用又有着世界上最大的用户群--尽管大多数用户并不知道,当他们进行谷歌搜索或者触摸安卓手机的时候,他们其实正在使用 Linux.如果不是 Linux 的巨大成功,Apple 公司也可能并不会成为现在这样(即在他们的电脑产品中使用 BSD 的技术

Linux用户空间与内核空间数据传递

转载 - Linux用户空间与内核空间数据传递 作者 digoal 日期 2016-11-20 标签 Linux , 用户空间 , 内核空间 , 用户态 , 内核态 , 进程上下文 , 中断上下文 背景 原文 http://wdqfirst.blog.163.com/blog/static/113347411201182222252954/ 原文 Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据