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 ...
随机推荐
- 用Go实现RabbitMQ消息收发
// amqp.Dial accepts a string in the AMQP URI format and returns a new Connection over TCP using Pla ...
- Snowflake Snow Snowflakes【Poj3349】
Description You may have heard that no two snowflakes are alike. Your task is to write a program to ...
- 笔记-python-语法-property
笔记-python-语法-property 1. property 看到@property,不明白什么意思,查找文档了解一下. 1.1. property类 proerty是pytho ...
- wlr快捷键
ref:http://www.cnblogs.com/zhangyang/archive/2011/07/22/2113856.html Windows Live Writer提供了许多方便的快捷 ...
- Android 网络通用类 NetUtil
1.整体分析 1.1.源代码如下,可以直接Copy. public class NetUtil { /** * 用户是否连接网络 * * @param context Context */ publi ...
- JS 金钱格式化
JavaScript Money Format(用prototype对Number进行扩展) Number.prototype.formatMoney = function (places, symb ...
- Field 'flag' doesn't have a default value错误
错误代码: java.sql.SQLException: Field 'flag' doesn't have a default value at com.mysql.jdbc.SQLError.cr ...
- Eclipse 创建 Java 项目---Eclipse教程第08课
打开新建 Java 项目向导 通过新建 Java 项目向导可以很容易的创建 Java 项目.打开向导的途径有: 通过点击 "File" 菜单然后选择 New > Java P ...
- web.xml 文件详解
目录 1. web.xml各版本区别 2. web.xml配置详解 2.1 java web项目启动加载顺序 2.2 web.xml中定义的元素 web.xml文件是Java Web项目中的一个配置文 ...
- postman与charles的结合使用
1.准备charles环境 Charles端口一般配置的为8888,不知道怎么配置详见charles文档 打开charles,发现访问浏览器任意页面都是失败. 在浏览器的高级设置中设置代理服务器,以火 ...