ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

Linux 内存管理一

2020-01-31 10:37:39  阅读:530  来源: 互联网

标签:DMA zone 管理 访问 内核 Linux 3G 内存


疫情在家,整理下以前的学习笔记, 作为linux 三个最重要的部分之一(进程,io,内存),内存管理是非常重要的,是深入理解linux各个部分的基础,linux的内存管理与其他rtos的内存管理不一样,他是一个“富” os,也就是支持很多的应用同时跑,还需要支持应用之间的内存隔离。Linux 内存不仅仅用于内存,比如作为硬盘的补充,硬盘本身也可以作为内存来使用。

 

  1. 硬件原理和分页管理

   只要我们打开了MMU之后,CPU只能看到虚拟地址,最终这个虚拟地址通过MMU根据页表查询到对应的硬件地址。比如要访问虚拟地址0x1234560,其中0x560 是页内偏移, 0x1234 是页号, 比如查到0x1234 对应的物理地址是1G,CPU访问0x1234560  的时候,实际访问的是1G+0x560 的地址。

物理地址是页表下面的一个数值,所以物理地址本质上是一个整数,而不是指针。地址是以*p访问到的,其实从linux中物理地址的数据类型也可以看出来

页表除了可以查虚拟地址对应的物理地址以外,还承担了一项非常重要的权限管理(RWX),RWX指读写执行权限,对linux安全非常重要,比如代码段映射为只读读加可执行,那么无论是应用还是内核里面的任何错误行为都不会改写代码段,只要一改写硬件就会发生page fault,软件的错误行为就会被硬件拦截,硬件里面还可以管理另外一个重要的权限,当在用户态运行的时候是不能访问内核态的东西的。 在用户态的时候处于CPU的非特权模式,在32位的系统里面,3G到4G 在页表里面填的是只有在特权模式才能访问,一旦被用户模式访问,硬件就会报错,产生page fault。比如应用写一个const 变量,这个const 变量地址会映射为read-only -->wiret --> page fault-->检查illegal(writing const var)-->signal segmentatino fault --> 进程退出。

 通常用户态是无法访问内核态的内容的,但是之前产生了一个熔断漏洞(meltaldown),这个漏洞为什么引起很大恐慌,因为利用这个漏洞,用户态突破了内核的界限,使得应用可以访问到内核态的内容,但是其实他并没有突破MMU的权限保护,他利用的是旁敲侧击的旁路攻击。

Meltdown 攻击的原理:

比如我们在应用程序中定义一个256个字节的数组,每个数组成员为4096 ,是为了使得每个成员隔得很远,防止CPU cache命中其他成员。k是一个内核地址,我们访问k的时候,会产生page fault,但是cpu是不会停止的,a[c]其实已经访问了,cache中命中了。此时我们还是不知道c是多少,接下来我们利用for循环把a中所有成员读一遍,看看哪个成员访问最快,结果发现就a[c] 比较快,其他都很慢,说明k里面的内容就c。这就是基于时间的旁路攻击。Meltdown 硬件从头到尾都没有让你真正读到,我们基于时间探测,推测出去。

这种旁路攻击很常见,比如以前我们破解用户名和密码,我们用破解软件尝试各种用户名,一般用户名不匹配就直接返回错误,不会check 密码,当用户名对的时候,就会去check 密码,这样我们查询突然有一个用户名返回时间比较久,说明这个用户是正确的用户名,再去尝试不同的密码,从而通过旁路攻击找到正确的用户名和密码。

 

内存zone

从上面我们知道内存是按照一页页进行管理,之所以按照页进行管理是因为MMU在管理内存按照页管理,linux还有一个概念叫做分zone。对于一个典型的32位系统按照下面分zone:

X86 平台入下图:arm 会不一样,因为arm里面32M的调用是短跳转,超过32M就是长跳转,开销大,3G-16M就是内核空间, 3G-16M 3G-2M之间就是内核用来放内核模块的,内核的代码又再3G - 3G+6M 之间。

DMA 是可以不需要CPU干预直接访问内存。为什么有个DMA zone,这个其实是历史遗漏原因,有一些比较老的外设(DMA有缺陷的)的地址总线只能范围32M的内存,所以DMA ZONE 存在的目的是保证内存一定是在32M或者更小的内存区域,从而保证DMA总线能够正常访问内存。这种硬件申请DMA zone的内存需要带一个GFP_DMA 标记,没有缺陷的硬件就不需要这个标记。现在的很多arm芯片,里面的外设控制器都没有这样的缺陷,所以很多情况下都不需要定义DMA zone。

还有一个high memory zone,为什么又这个HIGH ZONE ,在一个典型的32位系统 3G-4G 是内核空间, 0-3G是用户空间,内核空间总共只有1G,linux开机的时候会把做一个一一映射到内核空间,比如内存条有4G,怎么访问到所有的内存呢? linux 就创造了一个high memory。DMA zone NORMAL ZONE 都是低端内存,剩余的HIGH ZONE 就是高端内存。低端内存都是开机线性映射的,可以通过phys_to_virt 或者virt_to_phys 就可以访问。高端内存是运行时决定映射到哪个位置的,他不是线性映射,所以不能使用上的两个apis。 内核一般不从high memory 申请,因为内核使用的内存有限,一般normal内存就够了,但是应用程序一般是先从high memory 里面找,没有再从normal memory ,还没有再从dma zone ,按照这个顺序。对于arm 内核如果非要使用high memory ,一般映射到3G-2M 到3G的虚拟机中访问。

Buddy算法:

上面讲的每个zone使用管理算法都是Buddy算法,Buddy是按照2的n次方去管理内存的。这种算法牛在任何正整数都可以差分为2的n次方的和,所有linux最底层的算法一定都来自buddy(alloc_pages get_free_pages

buddy包含所有内存的使用情况,所有我们再proc/budyinfo 里面就可以查看这些信息

对于64位的处理器里面是没有low memory和high memory 的概念,因为64 位的处理,寻址空间2的64次方,可以映射到所有的内存,所以所有的内存都可以在内核里面线性映射。

Buddy 算法是用于自洽的,但是他有一个问题,他跑着跑着就跑散了,也就是内核碎片,就可能存在一个问题,比如电脑有100M的空闲内存,比如你需要16M的连续内存可能申请不到了,谁会需要连续的内存,应用程序只需要虚拟连续就可以了,只有DMA引擎 需要连续的内存,可是内存都散了,申请16M的连续内存可能就失败了。所以在工程上,一般的做法是直接预留内存(存在一定的乱费)。而更高级的做法是CMA的方式。

CMA 最早是三星公司提交的patch,CMA可以是的这些CMA区域(dts里面通常可以指定)的内存平时分配给应用程序使用,应用程序的内存一般是movable 的,当摄像头的DMA 需要申请连续内存的时候,就需要把应用程序的占用的CMA的内存挤走,就导出申请小的4k页,接着把应用程序占用CMA区域内容调到新申请的4k页中,接着改下应用程序的页表,这样应用程序察觉不到内存发生变化,从而预留出足够的CMA连续内存给摄像头使用了。这就是CMA算法巧妙的地方,这样即不乱费内存,有不影响DMA 使用。

MJ牧笛 发布了32 篇原创文章 · 获赞 8 · 访问量 7万+ 私信 关注

标签:DMA,zone,管理,访问,内核,Linux,3G,内存
来源: https://blog.csdn.net/mj5742356/article/details/104121565

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有