本文转载自:https://blog.csdn.net/juS3Ve/article/details/80035751

摘要

MMU与分页机制

内存区域(内存分ZONE)

LinuxBuddy分配算法

CMA(连续内存分配器)

0.    课前阅读

宋宝华:CPU是如何访问到内存的?--MMU最基本原理

http://mp.weixin.qq.com/s/SdsT6Is0VG84WlzcAkNCJA

宋宝华: 用代码切身实践体会meltdown漏洞

http://mp.weixin.qq.com/s/lJJU3LCepJgNq5AxyFFM8Q

投机之殇——一幅图读懂MELTDOWN漏洞

http://mp.weixin.qq.com/s?__biz=MzA3NTk5MDIzNw%3D%3D&mid=2647665589&idx=1&sn=3c29a6c3fe477cbd3b2efed983b1dd5e&scene=45#wechat_redirect

  1. MMU与分页机制

MMU(内存管理单元)是一个硬件,辅助操作系统进行内存管理,提供虚拟地址和物理地址的映射、内存访问权限保护和Cache缓存控制等硬件支持。CPU一旦开启MMU,CPU就只能看到虚拟地址(程序员也只能看到虚拟地址),只有MMU能看到物理地址。

MMU的主要功能如下:

1)提供虚拟地址和物理地址的映射

例如,CPU访问一个32位的虚拟地址0x12345 670,假设MMU的管理把每一页的内存分成4K(在32位系统中通常采用的PageSize为4K),上图中p(页号)即为12345,d(页内偏移地址)即为670。首先用p去查页表(页表本身也在内存),找到对应的页表项,页表项里会填写这一页虚拟地址所对应的物理地址。

注:基址寄存器存页表的基地址,每次进程切换时,寄存器的值会改变,因为每一个进程的页表都不一样。

2)内存访问权限保护

每个页表项中除了有虚拟地址所对应的物理地址外,还有这一页地址的RWX权限。比如,代码段只有R+X,用一个指针去写代码段,就会发生page fault(两种情况都会page fault:i. 虚拟地址没有找到对应的物理地址;ii. 虚拟地址有对应的物理地址,但权限不对)。EG. 写const变量page fault

另外一个重要的地方是,在MMU的页表项中还可以标注这一页的另一个并行的权限,即这个虚拟地址可以在用户态与内核态访问还是只能在内核态访问。例如,IA32下,内核空间地址一般映射到3GB~4GB,在页表项中就把3  G以上的页表设置为只有当CPU陷入到内核模式才能访问。这样就限制了用户态程序对内核数据的访问。 EG. Meldown旁路攻击,突破硬件限制。

附注:

i.          “虚拟地址是个指针,物理地址是个整数(32位或者64位整数,不是指针)“

可参考内核代码对物理地址的定义:include/linux/types.h

ii.          由于页表访问速度很慢,引出MMU的核心部件TLB(Translation Looksize Buffer),TLB是MMU的核心部件,它缓存少量的虚拟地址与物理地址的转换关系,是转换表的Cache,俗称“快表”。

当TLB中没有缓冲对应的地址转换关系时,需要通过对内存中转换表(大多数处理器的转换表为多级页表)的访问来获得虚拟地址和物理地址的对应关系,引出MMU的另一核心部件TTW(TranslationTable walk)。TTW成功后,结果应写入TLB中。

iii.          MPU:内存保护单元,只能做权限管理,不能做虚实映射。

2.   内存区域(内存分ZONE)

1)ZONE_DMA

DMA相对于内存与CPU相对于内存一样,可以直接访问内存。由于某些DMA引擎可能有缺陷,并不一定能访问到所有内存(如,x86 ISA总线上的DMA引擎在访问内存时,地址线只能发到16M以下,其硬件根本访问不了16M以上的内存),因此才分会ZONE_DMA。ZONE_DMA分多大由硬件决定,例如,某些体系结构在内存的任何地址上执行DMA都没有问题,在这些体系结构上,ZONG_DMA为空。

ZONE_DMA的内存不是专用于DMA的,而是有缺陷的DMA要申请内存时,从这个区域申请,如某个驱动模块调用void*dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_tgfp);函数申请一个ZONE_DMA内存区域时,需要将gfp参数写为GFP_DMA。

2)ZONE_HIGHMEM,ZONG_NORMAL

IA32下,0~3G是用户空间的虚拟地址,3G~4G是内核空间的虚拟地址。Linux为了使内核访问内存简单化,一开机就把一段物理地址直接线性映射到3G以上的虚拟地址。注意,物理内存可能大于1G,是无法全部线性映射到虚拟3G~4G的虚拟地址空间的,所以,Linux直接在物理内存上做界限(32位x86系统这个界限为896MB),低于这个界限的才做一一映射,低于这个界限的称为low memory,高于这个界限的称为high memory。low memory包含了ZONG_NORMAL+ZONE_DMA。注意:low memory虽然开机即线性映射,但并不意味着low memory已经被内核用掉,内核要使用内存时与应用程序一样是需要申请的。

low memory的物理地址和虚拟地址是一个直接的线性映射,可以使用内核API: phys_to_virt和virt_to_phys在物理地址和虚拟地址之间直接映射,而high memory则不能用这两个API。

内核空间一般不使用high memory,kmalloc申请的内存一般都在low memory。内核空间使用high memory时调用kmap进行映射。x86会把highmemory映射到虚拟地址3G以上,而ARM会映射到3G-2M~3G。如下两图所示:

综上,high memory产生的原因是不可能在虚拟地址映射区将所有的物理地址都做一一映射;ZONE_DMA产生的原因是DMA引擎硬件缺陷导致。x86-32上分区总结如下:

注意,区的划分没有任何物理意义,只不过是内核为了管理页而采取的一种逻辑上的分组。

3.    Linux Buddy分配算法

DMA、常规、高端内存这3个区域都采用buddy算法进行管理,把空闲的页以2的n次方为单位进行管理,因此Linux最底层的内存申请都是以2n 为单位的。Buddy算法最主要的的特点任何时候区域里的空闲内存都能以2的n次方进行拆分或合并。

例如,假设ZONE_NORMAL有16页内存(24),此时有人申请一页内存,Buddy算法会把剩下的15页拆分成8+4+2+1,放到不同的链表中去。此时再申请4页,直接给4页,若再申请4页,则从8页中给4页,正好剩下4页。Buddy算法的精髓在于任何正整数都可以拆分成2的n次方之和。

通过/proc/buddyinfo可以看到内存空闲的一些情况:

4.    CMA(连续内存分配器)

Buddy算法会导致内存碎片化,引出CMA技术。

应用程序的虚拟地址映射到哪个物理地址,物理地址连续与否都没有关系。但DMA引擎中没有MMU,有时候是需要连续物理内存的。比如,一个camera中有一个DMA,需要用DMA将拍摄的一张图片从camera搬移到内存,这时DMA申请连续16M内存进行搬移,但此时即使物理内存空闲100M(但不连续),DMA也申请不到的。

为了解决camera例子的问题,Linux可以一开机就将16M内存预留给camera,即使不用也占着浪费掉,而CMA技术就是为了避免内存浪费,使16M内存不预留,应用程序可以用,一旦camera要用,则CMA把这16M内存挤出来给camera。

由于应用程序的虚拟地址映射到哪个物理地址,连续与否都无所谓,甚至将虚拟地址move到别的物理地址都没关系,只要虚拟地址不变应用程序都不会察觉,所以一般应用程序申请内存时可以附带movable标记,Linux就可以把带movable标记的应用程序的内存申请放到CMA区域中。当camera DMA申请内存时,就“漫山遍野“申请很多4K(一页)内存,把movable的应用程序的物理地址搬移到这些内存中去,注意搬移内存会修改应用程序的页表项,虽然虚拟地址不变,但到物理地址的映射已经改变。这样之前的16M内存就被挤出来给camera的DMA了。

注意CMA是与DMA的API结合起来使用的,当使用DMA的API来申请内存时,才会触发CMA机制。

可以在dts中指定哪一段内存做CMA,既可以指定一个默认全局的CMA池,也可以给某一个特定的设备指定一个CMA池。具体实现方法详见内核文档Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt

郝健: Linux内存管理学习笔记-第1节课【转】的更多相关文章

  1. 郝健: Linux内存管理学习笔记-第2节课【转】

    本文转载自:https://blog.csdn.net/juS3Ve/article/details/80035753 摘要 slab./proc/slabinfo和slabtop 用户空间mallo ...

  2. Linux内存管理学习笔记 转

    https://yq.aliyun.com/articles/11192?spm=0.0.0.0.hq1MsD 随着要维护的服务器增多,遇到的各种稀奇古怪的问题也会增多,要想彻底解决这些“小”问题往往 ...

  3. Linux内存管理学习笔记——内存寻址

    最近开始想稍微深入一点地学习Linux内核,主要参考内容是<深入理解Linux内核>和<深入理解Linux内核架构>以及源码,经验有限,只能分析出有限的内容,看完这遍以后再更深 ...

  4. Linux内存管理学习笔记--物理内存分配

    http://blog.chinaunix.net/uid-20321537-id-3466022.html

  5. C++内存管理学习笔记(7)

    /****************************************************************/ /*            学习是合作和分享式的! /* Auth ...

  6. C++内存管理学习笔记(5)

    /****************************************************************/ /*            学习是合作和分享式的! /* Auth ...

  7. C++内存管理学习笔记(6)

    /****************************************************************/ /*            学习是合作和分享式的! /* Auth ...

  8. Linux内存管理学习资料

    下面是Linux内存管理学习的一些资料. 博客 mlock() and mlockall() system calls. All about Linux swap space 逆向映射的演进 Linu ...

  9. C++内存管理学习笔记(4)

    /****************************************************************/ /*            学习是合作和分享式的! /* Auth ...

随机推荐

  1. vue相关知识

    1.看https://www.bilibili.com/video/av27969216/?p=54,看他的就够了 https://juejin.im/post/5a5bc8c36fb9a01ca26 ...

  2. 【Python】向函数传递任意数量的实参

    传递任意数量的实参 有时候,你预先不知道函数需要接受多少个实参,好在Python允许函数从调用语句中收集任意数量的实参 def get_letter(*letters): for i in lette ...

  3. storm - 可靠机制

    一 可靠性简单介绍                    Storm的可靠性是指Storm会告知用户每个消息单元是否在一个指定的时间(timeout)内被全然处理. 全然处理的意思是该MessageI ...

  4. 朴素贝叶斯分类算法-----java

    1.贝叶斯分类的基础--贝叶斯定理 已知某条件概率.怎样得到两个事件交换后的概率,也就是在已知P(A|B)的情况下怎样求得P(B|A). 这里先解释什么是条件概率: 表示事件B已经发生的前提下,事件A ...

  5. Linux系统下授权MySQL账户访问指定数据库和数据库操作

    Linux系统下授权MySQL账户访问指定数据库 需求: 1.在MySQL中创建数据库mydata 2.新建MySQL账户admin密码123456 3.赋予账户admin对数据库mydata具有完全 ...

  6. Iconfot阿里妈妈-css高级应用

    矢量图标替换教程 首先进入:http://www.iconfont.cn/ 搜索你分类的关键字---然后加入购物车 加入购物车之后,下载到本地用浏览器打开demo.html 把a class=“原来样 ...

  7. Eclipse工程前面有个红色的感叹号的解决办法

    今天从SVN下载下工程之后,编译完,发现有两个工程有个红色的感叹号,一直没找到什么原因,问百度老师,发现问题的解决办法了. 1.先在控制台上点击Problems 如果控制台没有Problems,点击工 ...

  8. Java源代码之LinkedHashMap

    Java源代码之LinkedHashMap 转载请注明出处:http://blog.csdn.net/itismelzp/article/details/50554412 一.LinkedHashMa ...

  9. mysql 相关博客地址

    MySQL概念学习与逐步上手操作系列(一套完整)   https://www.cnblogs.com/zlslch/category/962962.html

  10. Docker入门系列8

    commit docker commit -m "Added json gem" -a "Docker Newbee" 0b2616b0e5a8 ouruser ...