li {list-style-type:decimal;}ol.wiz-list-level2 > li {list-style-type:lower-latin;}ol.wiz-list-level3 > li {list-style-type:lower-roman;}blockquote {padding:0 12px;padding:0 0.75rem;}blockquote > :first-child {margin-top:0;}blockquote > :last-child {margin-bottom:0;}img {border:0;max-width:100%;height:auto !important;margin:2px 0;}table {border-collapse:collapse;border:1px solid #bbbbbb;}td, th {padding:4px 8px;border-collapse:collapse;border:1px solid #bbbbbb;min-height:28px;word-break:break-all;box-sizing: border-box;}.wiz-hide {display:none !important;}
-->
span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }.cm-searching {background: #ffa; background: rgba(255, 255, 0, .4);}.cm-force-border { padding-right: .1px; }@media print { .CodeMirror div.CodeMirror-cursors {visibility: hidden;}}.cm-tab-wrap-hack:after { content: ""; }span.CodeMirror-selectedtext { background: none; }.CodeMirror-activeline-background, .CodeMirror-selected {transition: visibility 0ms 100ms;}.CodeMirror-blur .CodeMirror-activeline-background, .CodeMirror-blur .CodeMirror-selected {visibility:hidden;}.CodeMirror-blur .CodeMirror-matchingbracket {color:inherit !important;outline:none !important;text-decoration:none !important;}
-->

作者

彭东林
QQ 405728433
 

平台

Linux-4.10.17
Qemu-2.8 + vexpress-a9

概述

前面两篇介绍了remap_pfn_range的使用,下面学习一下该函数的实现。
 

正文

前提:下面的分析基于2级页表
 
remap_pfn_range的实现在mm/memory.c。
  1. int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
  2. unsigned long pfn, unsigned long size, pgprot_t prot)
  3. {
  4. pgd_t *pgd;
  5. unsigned long next;
  6. unsigned long end = addr + PAGE_ALIGN(size);
  7. struct mm_struct *mm = vma->vm_mm;
  8. unsigned long remap_pfn = pfn;
  9. int err;
  10.  
  11. /*
  12. * Physically remapped pages are special. Tell the
  13. * rest of the world about it:
  14. * VM_IO tells people not to look at these pages
  15. * (accesses can have side effects).
  16. * VM_PFNMAP tells the core MM that the base pages are just
  17. * raw PFN mappings, and do not have a "struct page" associated
  18. * with them.
  19. * VM_DONTEXPAND
  20. * Disable vma merging and expanding with mremap().
  21. * VM_DONTDUMP
  22. * Omit vma from core dump, even when VM_IO turned off.
  23. *
  24. * There's a horrible special case to handle copy-on-write
  25. * behaviour that some programs depend on. We mark the "original"
  26. * un-COW'ed pages by matching them up with "vma->vm_pgoff".
  27. * See vm_normal_page() for details.
  28. */
  29. vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
  30.  
  31. BUG_ON(addr >= end);
  32. pfn -= addr >> PAGE_SHIFT;
  33. pgd = pgd_offset(mm, addr);
  34. flush_cache_range(vma, addr, end);
  35. do {
  36. next = pgd_addr_end(addr, end);
  37. err = remap_pud_range(mm, pgd, addr, next,
  38. pfn + (addr >> PAGE_SHIFT), prot);
  39. if (err)
  40. break;
  41. } while (pgd++, addr = next, addr != end);
  42.  
  43. return err;
  44. }
第2行,pfn是将要被映射的物理页帧号,size表示需要映射的尺寸
第6行,计算本次映射的结尾虚拟地址
第32行的pfn-=addr>>PAGE_SHIFT,和第38行的pfn+(addr>>PAGE_SHIFT)是为了循环处理上的便利
第33行,计算addr在第1级页表中对应的页表项的地址,pgd_offset宏展开后是:mm->pgd + (addr >>21)
第34行,刷新cache
第36行,pgd_addr_end(addr, end)计算下一个将要被映射的虚拟地址,如果addr到end可以被一个pgd映射的话,那么返回end的值
第37行的remap_pud_range的定义如下:
  1. static inline int remap_pud_range(struct mm_struct *mm, pgd_t *pgd,
  2. unsigned long addr, unsigned long end,
  3. unsigned long pfn, pgprot_t prot)
  4. {
  5. pud_t *pud;
  6. unsigned long next;
  7.  
  8. pfn -= addr >> PAGE_SHIFT;
  9. pud = pud_alloc(mm, pgd, addr);
  10. if (!pud)
  11. return -ENOMEM;
  12. do {
  13. next = pud_addr_end(addr, end);
  14. if (remap_pmd_range(mm, pud, addr, next,
  15. pfn + (addr >> PAGE_SHIFT), prot))
  16. return -ENOMEM;
  17. } while (pud++, addr = next, addr != end);
  18. return ;
  19. }
第9行,对于2级页表,pud_alloc(mm, pgd, addr)返回的是pgd的值
第13行,对于2级页表,pud_addr_end(addr, end)返回end的值
第14行,函数remap_pmd_range定义如下:
  1. static inline int remap_pmd_range(struct mm_struct *mm, pud_t *pud,
  2. unsigned long addr, unsigned long end,
  3. unsigned long pfn, pgprot_t prot)
  4. {
  5. pmd_t *pmd;
  6. unsigned long next;
  7.  
  8. pfn -= addr >> PAGE_SHIFT;
  9. pmd = pmd_alloc(mm, pud, addr);
  10. if (!pmd)
  11. return -ENOMEM;
  12. VM_BUG_ON(pmd_trans_huge(*pmd));
  13. do {
  14. next = pmd_addr_end(addr, end);
  15. if (remap_pte_range(mm, pmd, addr, next,
  16. pfn + (addr >> PAGE_SHIFT), prot))
  17. return -ENOMEM;
  18. } while (pmd++, addr = next, addr != end);
  19. return ;
  20. }
第9行,对于2级页表,pmd_alloc(mm, pud, addr)返回的是pud的值,其实也就是pgd的值
第14行,对于2级页表,pmd_addr_end(addr, end)返回end的值
第15行,函数remap_pte_range定义如下:
  1. static int remap_pte_range(struct mm_struct *mm, pmd_t *pmd,
  2. unsigned long addr, unsigned long end,
  3. unsigned long pfn, pgprot_t prot)
  4. {
  5. pte_t *pte;
  6. spinlock_t *ptl;
  7.  
  8. pte = pte_alloc_map_lock(mm, pmd, addr, &ptl);
  9. if (!pte)
  10. return -ENOMEM;
  11. arch_enter_lazy_mmu_mode();
  12. do {
  13. BUG_ON(!pte_none(*pte));
  14. set_pte_at(mm, addr, pte, pte_mkspecial(pfn_pte(pfn, prot)));
  15. pfn++;
  16. } while (pte++, addr += PAGE_SIZE, addr != end);
  17. arch_leave_lazy_mmu_mode();
  18. pte_unmap_unlock(pte - , ptl);
  19. return ;
  20. }
第8行,pte_alloc_map_lock的定义如下:
  1. #define pte_alloc_map_lock(mm, pmd, address, ptlp) \
  2. (pte_alloc(mm, pmd, address) ? \
  3. NULL : pte_offset_map_lock(mm, pmd, address, ptlp))
pte_alloc首先检查*pmd是否为空,如果为空的话,表示第2级页表还尚未分配,那么调用__pte_alloc分配一个页(其实是调用alloc_pages分配了一个page,也就是4KB),并将起始地址存放的*pmd中,其实就是*pgd。如果不出意外的话,pte_alloc返回0,这样pte_offset_map_lock就会被调用,返回address在第2级页表中的表项的地址
 
第14行,调用pte_mkspecial构造第2级页表项的内容,函数set_pte_at用于将表项内容设置到pte指向的第2级页表项中
第15行,计算下一个将要被映射的物理页帧号
第16行,计算第2级页表项中下一个将要被填充的表项的地址
 
 
==

内存映射函数remap_pfn_range学习——代码分析(3)的更多相关文章

  1. 内存映射函数remap_pfn_range学习——示例分析(1)

    span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }.CodeMirror ...

  2. 内存映射函数remap_pfn_range学习——示例分析(2)

    li {list-style-type:decimal;}ol.wiz-list-level2 > li {list-style-type:lower-latin;}ol.wiz-list-le ...

  3. 20155207 《网络对抗》exp4 恶意代码分析 学习总结

    20155207 <网络对抗> 恶意代码分析 学习总结 实践目标 1.是监控你自己系统的运行状态,看有没有可疑的程序在运行. 2.是分析一个恶意软件,就分析Exp2或Exp3中生成后门软件 ...

  4. 开源项目kcws代码分析--基于深度学习的分词技术

    http://blog.csdn.net/pirage/article/details/53424544 分词原理 本小节内容参考待字闺中的两篇博文: 97.5%准确率的深度学习中文分词(字嵌入+Bi ...

  5. 通过 thread dump 分析找到高CPU耗用与内存溢出的Java代码

    http://heylinux.com/archives/1085.html通过 thread dump 分析找到高CPU耗用与内存溢出的Java代码 首先,要感谢我的好朋友 钊花 的经验分享. 相信 ...

  6. Linux -- 内存控制之oom killer机制及代码分析

    近期,线上一些内存占用比較敏感的应用.在訪问峰值的时候,偶尔会被kill掉,导致服务重新启动.发现是Linux的out-of-memory kiiler的机制触发的. http://linux-mm. ...

  7. Android代码分析工具lint学习

    1 lint简介 1.1 概述 lint是随Android SDK自带的一个静态代码分析工具.它用来对Android工程的源文件进行检查,找出在正确性.安全.性能.可使用性.可访问性及国际化等方面可能 ...

  8. ISD9160学习笔记05_ISD9160语音识别代码分析

    前言 语音识别是特别酷的功能,ISD9160的核心卖点就是这个语音识别,使用了Cybron VR 算法. 很好奇这颗10块钱以内的IC是如何实现人家百来块钱的方案.且听如下分析. 本文作者twowin ...

  9. mimalloc内存分配代码分析

    这篇文章中我们会介绍一下mimalloc的实现,其中可能涉及上一篇文章提到的内容,如果不了解的可以先看下这篇mimalloc剖析.首先我们需要了解的是其整体结构,mimalloc的结构如下图所示   ...

随机推荐

  1. python內建模块之datetime

    from:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143193755 ...

  2. Socket心跳包机制【转】

    转自:https://blog.csdn.net/xuyuefei1988/article/details/8279812 心跳包的发送,通常有两种技术 方法1:应用层自己实现的心跳包 由应用程序自己 ...

  3. linux 串口驱动(三) 【转】

    转自:http://blog.chinaunix.net/uid-27717694-id-3495825.html 三.串口的打开在用户空间执行open操作的时候,就会执行uart_ops->o ...

  4. grep和sed匹配多个字符关键字的用法

    GNU sed和UNIX sed 写法不一样 匹配多个关键词,打印出匹配的行,效果类似于 grep grep hello\|world file > output 或者用扩展正则 grep -E ...

  5. mongo批量操作存在更新否则插入

    def save_data(ok_ps): ns = [] for ok in ok_ps: ok['last_use_time'] = 0 ok['protocol'] = 0 # 协议类型 0:h ...

  6. Windows 安装 Go语言开发环境以及使用

    下载安装包 下载地址:http://www.golangtc.com/download 32 位请选择名称中包含 windows-386 的 msi 安装包,64 位请选择名称中包含 windows- ...

  7. js中ajax异步问题

    1.JS的执行顺序问题 浏览器是按照从上到下的顺序解析页面,因此正常情况下,JavaScript脚本的执行顺序也是从上到下的,即页面上先出现的代码或先被引入的代码总是被先执行,即使是允许并行下载Jav ...

  8. C#用Oracle.DataAccess中连接Oracle要注意版本问题!

    客户端Oracle.DataAccess.dll与服务器版本不一致时,如下修改:1.在客户端Web.config中,增加如下配置:<runtime> <assemblyBinding ...

  9. DDD领域模型企业级系统Unity(五)

    添加程序集: 写一个接口: public interface IPlayer { void Play(); } 两个实现类: public class NewPlay : IPlayer { publ ...

  10. fstab文件详解

    挂载分区的位置 挂载点 分区格式 设置 备份自检 UUID=94e4e... / ext4 defaults,barrier=0 1 1 tmpfs /dev/shm tmpfs defaults 0 ...