郝健: Linux内存管理学习笔记-第1节课【转】
本文转载自: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漏洞
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节课【转】的更多相关文章
- 郝健: Linux内存管理学习笔记-第2节课【转】
本文转载自:https://blog.csdn.net/juS3Ve/article/details/80035753 摘要 slab./proc/slabinfo和slabtop 用户空间mallo ...
- Linux内存管理学习笔记 转
https://yq.aliyun.com/articles/11192?spm=0.0.0.0.hq1MsD 随着要维护的服务器增多,遇到的各种稀奇古怪的问题也会增多,要想彻底解决这些“小”问题往往 ...
- Linux内存管理学习笔记——内存寻址
最近开始想稍微深入一点地学习Linux内核,主要参考内容是<深入理解Linux内核>和<深入理解Linux内核架构>以及源码,经验有限,只能分析出有限的内容,看完这遍以后再更深 ...
- Linux内存管理学习笔记--物理内存分配
http://blog.chinaunix.net/uid-20321537-id-3466022.html
- C++内存管理学习笔记(7)
/****************************************************************/ /* 学习是合作和分享式的! /* Auth ...
- C++内存管理学习笔记(5)
/****************************************************************/ /* 学习是合作和分享式的! /* Auth ...
- C++内存管理学习笔记(6)
/****************************************************************/ /* 学习是合作和分享式的! /* Auth ...
- Linux内存管理学习资料
下面是Linux内存管理学习的一些资料. 博客 mlock() and mlockall() system calls. All about Linux swap space 逆向映射的演进 Linu ...
- C++内存管理学习笔记(4)
/****************************************************************/ /* 学习是合作和分享式的! /* Auth ...
随机推荐
- 同步I/O 和 异步I/O
所谓同步I/O是指在调用ReadFile.WriteFile等函数进行输入输出操作时,系统完毕了输入输出ReedFile.WriteFile才返回. 在操作系统进行I/O操作的过程上,用户态线程不能运 ...
- DB2 SQL Error: SQLCODE=-805, SQLSTATE=51002 解决方法
在操作大量数据时如果发生这种错误,说明不是db2 使用的 package没有绑定,而是 因为资源未释放,导致可以使用此package的资源不足,致使不能连接资源. 在程序中,对PreparedStat ...
- 【重点突破】——Canvas技术绘制音乐播放器界面
一.引言 在用Canvas练习制作了验证码之后,还有一个用Canvas技术很综合的练习——制作音乐播放器.在做这个练习的过程中,还有一个重要的观察点,那就是理解Canvas的一大问题. 二.要求 点 ...
- Mockito使用指南
转载请标明出处:http://blog.csdn.net/shensky711/article/details/52771493 本文出自: [HansChen的博客] mock和Mockito的关系 ...
- Linux下自动备份MySQL数据库详细操作步骤(转载)
环境说明操作系统:CentOSIP:192.168.150.214Oracle数据库版本:Oracle11gR2用户:root 密码:123456端口:3306数据库:ts_0.ts_1.ts_2.t ...
- 王立平--eclipse中改动android项目的版本
改动版本 1.右键-->properties 2.android.改动须要的版本 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzQyNTU ...
- 基于pcl 和 liblas 库 las与pcd格式(rgb点)相互转换(win10 VS2013 X64环境 )
#include <liblas/liblas.hpp> #include <iomanip> #include <iostream> #include <s ...
- Linux系统下授权MySQL账户访问指定数据库和数据库操作
Linux系统下授权MySQL账户访问指定数据库 需求: 1.在MySQL中创建数据库mydata 2.新建MySQL账户admin密码123456 3.赋予账户admin对数据库mydata具有完全 ...
- 封装CLLocationManager定位获取经纬度
创建调用方法,在.h文件里 #import <Foundation/Foundation.h> @interface RMMapLocation : NSObject { void (^s ...
- 开发ActiveX控件调用另一个ActiveX系列0——身份证识别仪驱动的问题
程序员要从0下表开始,这篇是介绍这个系列的背景的,没有兴趣的人可以直接跳过. 为什么要开发ActiveX控件 由于工作需要,我们开发了一个网站,使用了一款身份证识别仪的网页ActiveX(OCX)插件 ...