linux缺页异常处理--用户空间
用户空间的缺页异常可以分为两种情况--
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用户空间 限制吗,在Linux中限制用户空间
在Linux中限制用户的磁盘使用空间使用的是Quota,一般Quota在安装Linux时就已包括在内核中,不需要另行安装.Quota可以从两方面指定磁盘的储存限制: 使用者所能够支配的索引节点(ino ...
- Openwrt按键检测分析-窥探Linux内核与用户空间通讯机制netlink使用
首先看一下Openwrt系统中关于按键功能的使用和修改,以18.06版本为例 按键功能实现在脚本中, 比如18.06/package/base-files/files/etc/rc.button/re ...
- linux内核与用户空间的九种通信机制
目前Linux提供了9种机制完成内核与用户空间的数据交换,分别是内核启动参数.模块参数与 sysfs.sysctl.系统调用.netlink.procfs.seq_file.debugfs和relay ...
- 人间还是仙界?聊一聊linux系统的用户空间和内核空间
我们生活在人间,但<西游记>里提到,在天上还有一个仙界. 人间不知道仙界的存在:而仙界知道人间的存在,神仙也可以从仙界下凡到人间,但是被严格管控的. 软件设计的灵感其实都来自于生活:用户空 ...
- 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内核将用户空间抽象成struct vm_area_struct进行管理,每申请以个用户空间在内核中都会抽象成对应的vm_are_struct进行管理,同时为了区别不同进程的 ...
- linux 用户空间文件系统 filesystem in userspace fuse 简介
用户空间文件系统(Filesystem in Userspace,简称FUSE)是操作系统中的概念,指完全在用户态实现的文件系统.目前Linux通过内核模块对此进行支持.一些文件系统如ZFS,glus ...
- linux 内核空间 sy,在 Linux 下用户空间与内核空间数据交换的方式,第 1 部分: 内核启动参数、模块参数与sysf...
级别: 初级 燚 杨 (), 计算机科学硕士 2006 年 2 月 16 日 本系列文章包括两篇,它们文详细地介绍了 Linux 系统下用户空间与内核空间数据交换的九种方式,包括内核启动参数.模块参数 ...
- FUSE——用户空间文件系统
用户空间文件系统(Filesystem in Userspace,简称FUSE)是操作系统中的概念,指完全在用户态实现的文件系统. 目前Linux通过内核模块对此进行支持.一些文件系统如ZFS,glu ...
- 一种内核到用户空间的高效数据传输技术
级别: 中级 桂 剑 (guijian@cn.ibm.com), IBM 中国开发中心,Linux Performance 项目软件工程师 2006 年 12 月 28 日 Relay 是一种从 Li ...
最新文章
- cmd中实现scott的解锁和开锁以及授予dba权限
- 批量下载_Zip压缩包的方式
- SFIM起航——源于无聊
- Python源码剖析[16] —— Pyc文件解析
- 关于cp命令中拷贝所有的写法
- Java: String.split(....); 结果很意外
- Logistic回归模型原理
- WDS部署服务所用的PXE引导文件
- 在Matplotlib图中插入LaTex公式
- 配置ouster雷达过程
- 2022最新前端vue面试题
- 汽车CAN总线入门,通俗易懂
- Leetcode 5773:插入后的最大值
- Git之(三)辅助命令
- (杂)网易云歌单导入到apple music
- 22南工计算机学院新生培训最终章---ACM竞赛机制
- 微软Win10彻底封杀exFAT/FAT32磁盘,格式化只剩NTFS/REFS
- 汽车半导体FMEDA的挑战和解决方案
- python实现——文件操作(超详细)
- 苹果动态壁纸库怎么增加_苹果xr如何添加动态壁纸设置
热门文章
- [书目20080630]人一生要养成的50个习惯
- MySQL中的四种Key
- flume学习(七):自定义source
- 探究CSS中border-top属性的使用
- MAC 终端基本命令
- 【狂人小白】MyBatis.001 学习巴提斯!
- linux mint 向“显示(display)”面板添加没有提供的分辨率选项,使虚拟机中的linux mint可以全屏显示...
- 分享5个viewport相关的jQuery插件
- 用 servlet 来创建一个用户登录界面
- 第二百一十六节,jQuery EasyUI,Spinner(微调)组件