linux缺页异常处理--用户空间【转】
转自:http://blog.csdn.net/vanbreaker/article/details/7870769
版权声明:本文为博主原创文章,未经博主允许不得转载。
用户空间的缺页异常可以分为两种情况--
1.触发异常的线性地址处于用户空间的vma中,但还未分配物理页,如果访问权限OK的话内核就给进程分配相应的物理页了
2.触发异常的线性地址不处于用户空间的vma中,这种情况得判断是不是因为用户进程的栈空间消耗完而触发的缺页异常,如果是的话则在用户空间对栈区域进行扩展,并且分配相应的物理页,如果不是则作为一次非法地址访问来处理,内核将终结进程
下面来看do_page_fault()函数对用户空间缺页异常的处理
- dotraplinkage void __kprobes
- do_page_fault(struct pt_regs *regs, unsigned long error_code)
- {
- struct vm_area_struct *vma;
- struct task_struct *tsk;
- unsigned long address;
- struct mm_struct *mm;
- int write;
- int fault;
- tsk = current; //获取当前进程
- mm = tsk->mm; //获取当前进程的地址空间
- /* Get the faulting address: */
- address = read_cr2(); //读取CR2寄存器获取触发异常的访问地址
- ...
- ...
- ...
- ...
- vma = find_vma(mm, address);//试图寻找到一个离address最近的vma,vma包含address或在address之后
- /*没有找到这样的vma则说明address之后没有虚拟内存区域,因此该address肯定是无效的,
- 通过bad_area()路径来处理,bad_area()的主体就是__bad_area()-->bad_area_nosemaphore()*/
- if (unlikely(!vma)) {
- bad_area(regs, error_code, address);
- return;
- }
- /*如果该地址包含在vma之中,则跳转到good_area处进行处理*/
- if (likely(vma->vm_start <= address))
- goto good_area;
- /*不是前面两种情况的话,则判断是不是由于用户堆栈所占的页框已经使用完,而一个PUSH指令
- 引用了一个尚未和页框绑定的虚拟内存区域导致的一个异常,属于堆栈的虚拟内存区,其VM_GROWSDOWN位
- 被置位*/
- if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) {
- bad_area(regs, error_code, address);//不是堆栈区域,则用bad_area()来处理
- return;
- }
- if (error_code & PF_USER) {//必须处于用户空间
- /*
- * Accessing the stack below %sp is always a bug.
- * The large cushion allows instructions like enter
- * and pusha to work. ("enter $65535, $31" pushes
- * 32 pointers and then decrements %sp by 65535.)
- */
- /*这里检查address,只有该地址足够高(和堆栈指针的差不大于65536+32*sizeof(unsigned long)),
- 才能允许用户进程扩展它的堆栈地址空间,否则bad_area()处理*/
- if (unlikely(address + 65536 + 32 * sizeof(unsigned long) < regs->sp)) {
- bad_area(regs, error_code, address);
- return;
- }
- }
- if (unlikely(expand_stack(vma, address))) {//堆栈扩展不成功同样由bad_area()处理
- bad_area(regs, error_code, address);
- return;
- }
- /*
- * Ok, we have a good vm_area for this memory access, so
- * we can handle it..
- */
- good_area:
- write = error_code & PF_WRITE;
- /*访问权限不够则通过bad_area_access_error()处理,该函数是对__bad_area()的封装,只不过
- 发送给用户进程的信号为SEGV_ACCERR*/
- if (unlikely(access_error(error_code, write, vma))) {
- bad_area_access_error(regs, error_code, address);
- return;
- }
- /*
- * If for any reason at all we couldn't handle the fault,
- * make sure we exit gracefully rather than endlessly redo
- * the fault:
- */
- /*分配新的页表和页框*/
- fault = handle_mm_fault(mm, vma, address, write ? FAULT_FLAG_WRITE : 0);
- if (unlikely(fault & VM_FAULT_ERROR)) {
- mm_fault_error(regs, error_code, address, fault);
- return;
- }
- if (fault & VM_FAULT_MAJOR) {
- tsk->maj_flt++;
- perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0,
- regs, address);
- } else {
- tsk->min_flt++;
- perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0,
- regs, address);
- }
- check_v8086_mode(regs, address, tsk);
- up_read(&mm->mmap_sem);
- }
bad_area()函数的主体函数为__bad_area()-->__bad_area_nosemaphore(),这个函数在上一篇博文中分析了其对内核的非法访问的处理,现在看其对用户空间的非法访问的处理
- __bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code,
- unsigned long address, int si_code)
- {
- struct task_struct *tsk = current;
- /* User mode accesses just cause a SIGSEGV */
- /*错误发生在用户态,则向用户进程发送一个SIGSEG信号V*/
- if (error_code & PF_USER) {
- /*
- * It's possible to have interrupts off here:
- */
- local_irq_enable();
- /*
- * Valid to do another page fault here because this one came
- * from user space:
- */
- if (is_prefetch(regs, error_code, address))
- return;
- if (is_errata100(regs, address))
- return;
- if (unlikely(show_unhandled_signals))
- show_signal_msg(regs, error_code, address, tsk);
- /* Kernel addresses are always protection faults: */
- tsk->thread.cr2 = address;
- tsk->thread.error_code = error_code | (address >= TASK_SIZE);
- tsk->thread.trap_no = 14;
- force_sig_info_fault(SIGSEGV, si_code, address, tsk);
- return;
- }
- ...
- ...
- }
在确定了这次异常是因为物理页没分配而导致后,就通过good_area路径来处理,可想而知,该路径在确定了访问权限足够后,将完成页表和物理页的分配,这个任务有handle_mm_fault()函数来完成
- int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
- unsigned long address, unsigned int flags)
- {
- pgd_t *pgd;
- pud_t *pud;
- pmd_t *pmd;
- pte_t *pte;
- __set_current_state(TASK_RUNNING);
- count_vm_event(PGFAULT);
- if (unlikely(is_vm_hugetlb_page(vma)))
- return hugetlb_fault(mm, vma, address, flags);
- pgd = pgd_offset(mm, address);
- pud = pud_alloc(mm, pgd, address);//分配pud目录
- if (!pud)
- return VM_FAULT_OOM;
- pmd = pmd_alloc(mm, pud, address);//分配pmd目录
- if (!pmd)
- return VM_FAULT_OOM;
- pte = pte_alloc_map(mm, pmd, address);//分配pte表
- if (!pte)
- return VM_FAULT_OOM;
- /*handle_pte_fault()的任务就是为pte绑定新的页框,它会根据pte页表项的情况来做不同的处理*/
- return handle_pte_fault(mm, vma, address, pte, pmd, flags);
- }
handle_pte_fault()函数的处理比较复杂,因为它要根据pte页表项对应的物理页的不同状态来做各种不同的处理,具体的分析以后再给出。
linux缺页异常处理--用户空间【转】的更多相关文章
- linux缺页异常处理--内核空间
缺页异常被触发通常有两种情况-- 程序设计的不当导致访问了非法的地址 访问的地址是合法的,但是该地址还未分配物理页框. 下面解释一下第二种情况,这是虚拟内存管理的一个特性.尽管每个进程独立拥有3GB的 ...
- linux缺页异常处理--内核空间【转】
转自:http://blog.csdn.net/vanbreaker/article/details/7867720 版权声明:本文为博主原创文章,未经博主允许不得转载. 缺页异常被触发通常有两种情况 ...
- Linux环境下用户空间与内核空间数据的交换方式
在linux环境开发过程中,经常会需要在用户空间和内核空间之间进行数据交换. 介绍了 Linux 系统下用户空间与内核空间数据交换的几种方式 第一节:使用procfs实现内核交互简明教程(1) 第二节 ...
- 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. 参考代码 ...
- Linux调度器 - 用户空间接口
一.前言 Linux调度器神秘而充满诱惑,每个Linux工程师都想深入其内部一探究竟.不过中国有一句古话叫做“相由心生”,一个模块精巧的内部逻辑(也就是所谓的“心”)其外延就是简洁而优雅的接口(我称之 ...
- linux下在用户空间访问I/O端口的ioperm和iopl函数
1.ioperm函数 功能描述:为调用进程设置I/O端口访问权能.ioperm的使用需要具有超级用户的权限,只有低端的[0-0x3ff] I/O端口可被设置,要想指定更多端口的权能,可使用i ...
- Linux内存管理--用户空间和内核空间【转】
本文转载自:http://blog.csdn.net/yusiguyuan/article/details/12045255 关于虚拟内存有三点需要注意: 4G的进程地址空间被人为的分为两个部分--用 ...
- linux内存管理--用户空间和内核空间
关于虚拟内存有三点需要注意: 4G的进程地址空间被人为的分为两个部分--用户空间与内核空间.用户空间从0到3G(0xc0000000),内核空间占据3G到4G.用户进程通常情况下只能访问用户空间的虚拟 ...
- [转]五个Linux下用户空间的调试工具
有几个Linux下的用户空间调试工具和技术,它们用来分析用户空间的问题相当有用.它们是: 'print' 语句 查询 (/proc, /sys 等) 跟踪 (strace/ltrace) Valgri ...
随机推荐
- 4,MongoDB 之 $关键字 及 $修改器 $set $inc $push $pull $pop MongoDB
MongoDB中的关键字有很多, $lt $gt $lte $gte 等等,这么多我们也不方便记,这里我们说说几个比较常见的 一.查询中常见的 等于 大于 小于 大于等于 小于等于 等于 : 在Mon ...
- 关于 JS 模块化的最佳实践总结
模块化开发是 JS 项目开发中的必备技能,它如同面向对象.设计模式一样,可以兼顾提升软件项目的可维护性和开发效率. 模块之间通常以全局对象维系通讯.在小游戏中,GameGlobal 是全局对象.在小程 ...
- P2440 木材加工(二分答案)
P2440 木材加工 要保护环境 题目描述 题目描述: 木材厂有一些原木,现在想把这些木头切割成一些长度相同的小段木头(木头有可能有 剩余),需要得到的小段的数目是给定的.当然,我们希望得到的小段木头 ...
- Spring_依赖注入思想
Ioc(Inversion of Control) 控制反转,DI(Dependncy Injection)依赖注入,其实是指同一种思想.举例说明: 张三在某公司负责供水问题.有两种形式,第一种老板指 ...
- 【02】webstorm配置babel转换器+截图(by魔芋)
[02]webstorm配置babel转换器+截图(by魔芋) [02]魔芋的安装过程 01,配置babel. 02,用webstorm.注意webstorm的版本号. 03,使用 ...
- 有关ViewFlipper的使用及设置动画效果的讲解
说到左右滑动,其实实现左右滑动的方式很多,有ViewPaer,自定义实现Viewgroup,gallery等都可以达到这种效果.这里做下ViewFliper实现左右滑动的效果. 会用到以下的技术: 1 ...
- WPF 利用键盘钩子来捕获键盘,做一些不为人知的事情...完整实例
键盘钩子是一种可以监控键盘操作的指令. 看到这句话是不是觉得其实键盘钩子可以做很多事情. 场景 当你的程序需要一个全局的快捷键时,可以考虑使用键盘钩子,如大家常用qq的截图快捷键,那么在WPF里怎么去 ...
- 课时5:闲聊之Python的数据类型
目录: 一.引言 二.数据类型 >整型 >浮点型 >布尔类型 三.类型转换 四.获得关于类型的信息 五.课时05课后习题及答案 *********** 一.引言 ********** ...
- QQ网页强制聊天,微博一键关注
<!doctype html> <!-- 微博关注需要的js --> <html xmlns:wb="http://open.weibo.com/wb" ...
- Linux 查看当前日期和时间
一.查看和修改Linux的时区 1. 查看当前时区 命令 : "date -R" 2. 修改设置Linux服务器时区 方法 A 命令 : "tzselect" ...