c++ mmap写入速度_内存管理(24)mmap和缺页中断
相关文件:
mm/mlock.c
mm/util.c
mm/mmap.c
include/linux/slab.h
mmap系统调用陷入内核中会调用vm_mmap_pgoff=>do_mmap_pgoff函数来真正完成mmap操作
1.1.do_mmap_pgoff函数(关键部分代码)
do_mmap_pgoff
- 16行:对需要映射的内存长度页对齐
- 20行:get_unmapped_area返回需要映射的地址空间
- 25行:建立映射关系,如果定义了VM_LOCKED还要立即申请物理内存,该函数的实现细节请看1.2小节
1.2.mmap_region函数(关键部分代码)
mmap_region
- 40行:查找符合条件的vma
- 46行:合并相邻的vma
- 49行:如果没找到合适的vma则调用kmem_cache_zalloc申请一个vma结构,这个函数前面也说过
- 51行:把初始化好的vma插入mm下的内存管理树结构
- 55行:配置vma的访问权限
2.缺页中断(armv7架构)
相关文件:
mm/memory.c
arch/arm/mm/fault.c
缺页中断与具体的处理器架构密切相关。以ARM为例,页面转换失效和页面访问权限失效对应的核心处理函数为do_page_fault。
缺页中断基本流程
2.1.do_page_fault函数(关键部分)
do_page_fault函数-1
- 69行:处于中断上下文或者禁止抢占时跳转到no_context标签
- 71行:用户空间添加FAULT_FLAG_USER标志
- 73行:具备FSR_WRITE权限则添加FAULT_FLAG_WRITE标志
- 76行:真正处理缺页中断的函数,详情看小节2.2
do_page_fault函数-2
- 85行:不属于以上异常就说明缺页中断正常处理完成
- 88行:缺页中断处理有异常发生,如果是内核空间跳转到no_context标签执行__do_kernel_fault函数
- 90行:VM_FAULT_OOM则调用pagefault_out_of_memory发送OOM信号
- 95行:用户空间则向进程发送SIGSEGV信号,进程中断
- 99行:__do_kernel_fault函数发送Oops错误
2.2.__do_page_fault函数(关键部分)
__do_page_fault函数
- 112行:查找vma
- 115行:vma访问权限
- 120行:缺页中断处理,详见2.3
2.3.__handle_mm_fault函数(关键部分)
__handle_mm_fault函数
- 141~148行:获取到addr所对应的PGD、PUD、PMD、PTE
- 150行:handle_pte_fault处理缺页中断,详见2.4
2.4.handle_pte_fault函数(关键部分)
handle_pte_fault函数-1
176行:如果页面不在内存中(还未映射真正的页),调用pte_none177行:页面为空。对于文件映射通常会调用do_fault(详见2.6);否则表示匿名映射则会调用do_anonymous_page(详见2.5)186行:页面不为空,表示正处于交换内存中,调用do_swap_page函数,详见2.7189行:写异常190行:如果pte为只读权限,调用do_swap_page函数193行:如果具备写属性,L_PTE_DIRTY置位
handle_pte_fault函数-2
- 189行:设置L_PTE_YONG位
- 190行:如果pte页面项有变化就要写入物理页面项,并更新TLB cache
2.5.do_anonymous_page函数
do_anonymous_page函数-1
- 225行:判断当前VMA是否需要增加一个guard page作为安全垫
- 229~236行:对于只读的VMA,系统使用0号页面生成新的PTE entry,0号页面是在paging_init中初始化的,前面提过。使用pte_offset_map_lock能得到页表项。如果表项不为空则跳转到setpte处更新到硬件表中。
do_anonymous_page函数-2
241行:分配一个可写的匿名页面,最终调用伙伴系统的alloc_pages,优先选择高端内存254~256行:通过mk_xxx生成一个新的pte entry262行:inc_mm_counter_fast增加系统中匿名页面的引用计数263行增加到RMAP反向映射系统中265行:把匿名页添加到LRU链表中,kswap中会用到267行:设置pte entry到硬件页表中
do_anonymous_page函数-3
- 270行:刷新TLB和cache
2.6.do_fault函数
do_fault函数
- 300行:只读缺页异常,详见2.8小节
- 303行:私有映射且发生写时拷贝缺页异常,详见2.9小节
- 305行:公有映射写缺页异常,详见2.10小节
2.7.do_swap_page函数
与2.8和2.9类似
2.8.do_read_fault函数
static int do_read_fault(struct mm_struct *mm, struct vm_area_struct *vma,unsigned long address, pmd_t *pmd,pgoff_t pgoff, unsigned int flags, pte_t orig_pte){struct page *fault_page;if (vma->vm_ops->map_pages && fault_around_bytes >> PAGE_SHIFT > 1) {//拿到缺页异常地址addr所对应的ptepte = pte_offset_map_lock(mm, pmd, address, &ptl);/*以缺页异常地址为中心,从start_addr开始检查相应的pte是否为空,若为*空则从这个pte开始到max_pgoff为止使用map_pages()来映射PTE。这么做为*了提前建立缺页地址(进程地址空间)与(现存)page cache的映射关系,*减少缺页中断的次数从而提高效率。*/do_fault_around(vma, address, pte, pgoff, flags);//页面内容在刚刚被系统修改了,跳转到unlock_out标签if (!pte_same(*pte, orig_pte))goto unlock_out;pte_unmap_unlock(pte, ptl);}//为缺页异常地址分配page cache ret = __do_fault(vma, address, pgoff, flags, NULL, &fault_page);if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE | VM_FAULT_RETRY)))return ret;//拿到映射好的缺页地址的PTEpte = pte_offset_map_lock(mm, pmd, address, &ptl);if (unlikely(!pte_same(*pte, orig_pte))) {pte_unmap_unlock(pte, ptl);unlock_page(fault_page);page_cache_release(fault_page);return ret;}//为PTE创建条目,并加入反向映射机制do_set_pte(vma, address, fault_page, pte, false, false);unlock_page(fault_page);unlock_out://放弃映射pte_unmap_unlock(pte, ptl);return ret;}
2.9.do_cow_fault函数
//省略部分错误判断static int do_cow_fault(struct mm_struct *mm, struct vm_area_struct *vma,unsigned long address, pmd_t *pmd,pgoff_t pgoff, unsigned int flags, pte_t orig_pte){pte_t *pte;int ret;if (unlikely(anon_vma_prepare(vma)))return VM_FAULT_OOM;//以GFP_HIGHUSER_MOVABLE分配掩码分配一个物理页面new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, address);if (mem_cgroup_try_charge(new_page, mm, GFP_KERNEL, &memcg)) {page_cache_release(new_page);return VM_FAULT_OOM;}//读取文件内容到fault_pageret = __do_fault(vma, address, pgoff, flags, new_page, &fault_page);if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE | VM_FAULT_RETRY)))goto uncharge_out;if (fault_page)//拷贝内容到物理页面copy_user_highpage(new_page, fault_page, address, vma);__SetPageUptodate(new_page);//取得对应的PTEpte = pte_offset_map_lock(mm, pmd, address, &ptl);//为对应PTE创建条目再添加到反向映射机制do_set_pte(vma, address, new_page, pte, true, true);mem_cgroup_commit_charge(new_page, memcg, false);//物理页面加入LRU队列进行管理,内存回收会用到lru_cache_add_active_or_unevictable(new_page, vma);pte_unmap_unlock(pte, ptl);if (fault_page) {unlock_page(fault_page);page_cache_release(fault_page);} else {i_mmap_unlock_read(vma->vm_file->f_mapping);}return ret;uncharge_out:mem_cgroup_cancel_charge(new_page, memcg);page_cache_release(new_page);return ret;}
2.10.do_shared_fault函数
与2.8和2.9类似
2.11.do_fault_around函数
static void do_fault_around(struct vm_area_struct *vma, unsigned long address,pte_t *pte, pgoff_t pgoff, unsigned int flags){unsigned long start_addr, nr_pages, mask;pgoff_t max_pgoff;struct vm_fault vmf;int off;/*fault_around_bytes是一个全局变量,等于16个page的大小,所以此处nr_pages=16*/nr_pages = ACCESS_ONCE(fault_around_bytes) >> PAGE_SHIFT;//以16页对齐为步长计算掩码mask = ~(nr_pages * PAGE_SIZE - 1) & PAGE_MASK;//以16页对齐,取缺页地址和vm_start较大的那个为扫描PTE的起始地址start_addr = max(address & mask, vma->vm_start);/*PTRS_PER_PTE 表示每个PTE项所对应的条目数,此处为512也就是1个PTE对应512页*这一步相当于取缺页地址和start_addr的偏离(页数单位)的绝对值*/off = ((address - start_addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);//查找PTE的起始位置pte -= off;pgoff -= off;/* * max_pgoff is either end of page table or end of vma * or fault_around_pages() from pgoff, depending what is nearest. */max_pgoff = pgoff - ((start_addr >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) +PTRS_PER_PTE - 1;max_pgoff = min3(max_pgoff, vma_pages(vma) + vma->vm_pgoff - 1,pgoff + nr_pages - 1);/* Check if it makes any sense to call ->map_pages */while (!pte_none(*pte)) {if (++pgoff > max_pgoff)return;start_addr += PAGE_SIZE;if (start_addr >= vma->vm_end)return;pte++;}//找到为空的PTE,则从此处开始到max_pgoff映射PTEvmf.virtual_address = (void __user *) start_addr;vmf.pte = pte;vmf.pgoff = pgoff;vmf.max_pgoff = max_pgoff;vmf.flags = flags;vma->vm_ops->map_pages(vma, &vmf);}
c++ mmap写入速度_内存管理(24)mmap和缺页中断相关推荐
- Linux内存管理 brk(),mmap()系统调用源码分析2:brk()的内存释放流程
Linux brk(),mmap()系统调用源码分析 brk()的内存释放流程 荣涛 2021年4月30日 内核版本:linux-5.10.13 注释版代码:https://github.com/Rt ...
- Linux内存管理 brk(),mmap()系统调用源码分析1:基础部分
Linux内存管理 brk(),mmap(),munmap()系统调用源码分析 基础部分 荣涛 2021年4月30日 内核版本:linux-5.10.13 注释版代码:https://github.c ...
- Linux内存管理之mmap详解
一. mmap系统调用 1. mmap系统调用 mmap将一个文件或者其它对象映射进内存.文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零.munmap执行相 ...
- Linux内存管理子系统——mmap内存映射原理分析(dax文件系统的mmap)
Linux mmap分析 内核版本:linux-5.16 1. 虚拟内存概要及相关内容简介 内存映射是学习过操作系统的大家都耳熟能详的词,理解起来也很简单.所谓"映射"就是为一种事 ...
- linux 内存管理(15) - mmap
了解mmap机制.参考此处 1.概述 mmap 即地址的映射, 是一种内存映射文件的方法,将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系 ...
- c++ 读取内存数据 基址_内存管理(仅学习)
理解linux内存管理首先得理解内存映射. 程序用的都是逻辑地址,以下为objdump反汇编程序的结果,左边一列都是逻辑地址: 000000000040053c : 40053c: 55 push % ...
- 操作系统知识点_内存管理
2.内存管理: 内存管理方式: 块式管理:主存分为一大块一大块.易于管理,浪费太大: 页式管理:主存分为一页一页的,每一页比一块要小得多.显然其空间利用率比块式高很多.分页用户不可见. 段式管理:二维 ...
- 虚拟存储器管理c语言_内存管理;虚拟内存
内存管理 内存管理 操作系统的内存管理主要是做什么? 操作系统的内存管理主要负责内存的分配与回收(malloc 函数:申请内存,free 函数:释放内存),另外地址转换也就是将逻辑地址转换成相应的物理 ...
- 6.Python深入_内存管理
Vamei博客地址:http://www.cnblogs.com/vamei/p/3232088.html 哈,这次是完完全全照搬大神的了 语言的内存管理是语言设计的一个重要方面.它是决定语言性能的重 ...
最新文章
- servlet config 初始化参数
- saltstack一些常用模块和api调用方法
- SpringMVC学习03之使用注解开发SpringMVC
- Linux 下的dd命令使用详解(摘录)
- EXCEL 图表 只在拐点的时候显示数字
- Java虚拟机知识汇总,有这些还怕面试被问到?
- LeetCode113. 路径总和 II(DFS)(递归)
- linux 常用分区有哪些,Linux常用分区及目录
- OpenCV基本图形绘制之绘制直线
- 请求发送者与接收者解耦——命令模式(五)
- 如何使用USB摄像头搭配Visionpro进行视觉识别
- LabView实验——温度检测系统(实验学习版)
- modelica语言学习记录V1.0
- SAP注塑行业ERP系统软件的分析
- C语言 计算个人所得税
- Dynamics CRM: 权限问题之SecLib::AccessCheckEx2 failed
- 8个优秀图片素材库,免费/商用/高分辨率。
- 服务器安装与维护,服务器安装与维护 PPT课件
- 响应式移动端框架_简单,响应式,移动优先导航
- 最大熵模型怎么理解?熵是什么??
热门文章
- LeetCode 1019. 链表中的下一个更大节点(单调栈)
- LeetCode 437. 路径总和 III(双重递归)
- LeetCode 268. 缺失数字
- mysql的where字句调优_mysql中select和where子句优化的总结
- 【机器学习】sclearn分类算法-决策树、随机森林
- Ubuntu下svn 版本管理客户端工具及常用方法
- java执行程序默认多线程吗_Java多线程 执行程序(1)
- 训练效率低?GPU利用率上不去?快来看看别人家的tricks吧~
- 阿里架构师进阶23期精讲:Redis、Kafka、Dubbo、Docker等
- 论文浅尝 | 基于动态记忆的原型网络进行元学习以实现少样本事件探测