Linux内核之内存2: 内存的动态申请、释放的原理和细节
1.slab、kmalloc/kfree、/proc/slabinfo和slabtop
Buddy 是直面物理内存的,所有的内存分配,最终都通过Buddy的get_free_page/page_alloc分配;
Buddy的粒度太大,最小分配一页(4k); 而我们常常需要分配小内存;
所以Linux引入一个二级分配的概念:
1.内核分配内存,调用kmalloc()/kfree()–调用slab–再调用Buddy ;
2.用户空间malloc/free–调用C库–C库通过brk/mmap调用Buddy;
free释放的内存,只是释放给C库,未必真正释放;释放给C库的内存,其他进程无法使用,共享代码段,数据段独立;
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v7g0ilFE-1598498836053)(media/d6589cc9b8d5787afe1e294718e6aa9a.jpg)]](https://i-blog.csdnimg.cn/blog_migrate/e70647b21f8faee3c5d866937afd04ac.png#pic_center)
(1)slab原理:
就是从Buddy拿到一个/多个页,分成多个相同大小的块,比如进程控制块task_struct这种内核中常用的结构体,可以先从Buddy申请一个slab池,实际kmalloc分配的时候,直接从slab里分配。可以提高速度,也可以使内存得到充分使用,减少内存碎片;
每一个slab里的所有块,大小一定是相同的;
sudo cat /proc/slabinfo
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y8qwlwfd-1598498836056)(media/6144c4436689897672f92d235414ebd3.png)]](https://i-blog.csdnimg.cn/blog_migrate/35b093d28476e7adb2a23e2d5a7b0bf2.png#pic_center)
cat /proc/meminfo
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rd9b35HH-1598498836058)(media/a9b557f33ca99abfb767b2bba2cf710d.png)]](https://i-blog.csdnimg.cn/blog_migrate/d922a9a544549e564a7e7813f8f5bace.png#pic_center)
slab也是一个内存泄漏的源头;
应用程序free内存,未必一定释放,可以通过mallopt设置free内存的触发阈值,触发阈值,free才真正在Buddy
释放;
(2) kmalloc / vmalloc详细过程
(1)kmalloc从低端内存区域分配,该区域是开机就一次性映射好;所以kmalloc申请,不需要做映射, 且在物理内存上是连续的。
phys_to_virt/virt_to_phys只是一个简单的内存线性偏移。
(2)vmalloc申请的虚拟内存,可以从普通物理内存空间分配,也可以从低端内存分配;
调用vmalloc申请,会有一个虚拟地址到物理地址映射过程。
ioremap映射的也是vmalloc虚拟地址,不过不用申请内存,ioremap映射的是寄存器;
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s3BlwgXk-1598498836060)(media/14f91e046e015f8cce0f911ccb27953c.png)]](https://i-blog.csdnimg.cn/blog_migrate/ae02322b0a552ed89fb9d53e0cab41e1.png#pic_center)
映射跟分配是两回事,低端被映射之后,不一定被使用;
sudo cat /proc/vmallocinfo |grep ioremap
//可以查看虚拟地址映射物理地址/寄存器关系
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Do4QUq9x-1598498836061)(media/a956191954ae0fff88aadd2a06a6a5b7.png)]](https://i-blog.csdnimg.cn/blog_migrate/97646a89af46c9b1ff3eaec8ae31f2a2.png#pic_center)
kmalloc和vmalloc区别大概可总结为:
(1)vmalloc分配的一般为普通内存,只有当内存不够的时候才分配低端内存;kmalloc从低端内存分配。
(2)vmalloc分配的物理地址一般不连续,而kmalloc分配的物理地址连续,两者分配的虚拟地址都是连续的;
(3)vmalloc分配的一般为大块内存,而kmallc一般分配的为小块内存;
2.用户空间malloc/free与内核之间的关系
问题1:malloc:VSS , RSS
p = malloc(100M);//分配过程
1.在进程的堆中找到一个空闲地址,比如1G,创建一个VMA(virtual memory
area),权限可读写;
2.将p=1G~1G+100M全部映射到零页(标记为只读);
3.当读p虚拟内存的时候,全部返回0,实际上任何内存都未分配;
4.当写内存时,比如写1G+30M处,而该地址映射向零页(只读),MMU发现权限错误,报page fault,CPU可以从page fault中得到写地址和权限,检查vma中的权限,如果vma标明可写,那么触发缺页中断,内核分配一个4K物理页,重新填写1G+30M页表,使其映射到新分配的物理页;
这样该页就分配了实际的物理内存,其他所有未使用到的虚拟地址亦然映射到零页。
如果检查vma发现没有可写权限,则报段错误;
如果检查vma合法,但是系统内存已经不够用,则报out of memory(OOM).
在真正写的时候,才去分配,这是按需分配,或者惰性分配;
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fpiBTR2O-1598498836063)(media/76056b4cf5a5ac418057cb8ac36d185c.jpg)]](https://i-blog.csdnimg.cn/blog_migrate/0f7c861c1a74e67eefa3a1afb7609b0a.png#pic_center)
只申请了VMA,未实际拿到物理内存,此时叫VSS;拿到实际内存后是RSS(驻留内存);
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PxCi8frV-1598498836064)(media/b1afb692a76f8950bf78d3c2cca5934d.png)]](https://i-blog.csdnimg.cn/blog_migrate/98edd2ffd1c00f00c064034ac016c840.png#pic_center)
属于按需分配demanding page,或者惰性分配lazy allocation;
对于代码段(实际读时,才实际去分配内存,把代码从硬盘读到内存),数据段都是类似处理,实际使用时,才会实际分配内存;
3 内存耗尽(OOM)、oom_score和oom_adj
在实际分配内存,发现物理内存不够用时,内核报OOM,杀掉最该死进程(根据oom_score打分),释放内存;
打分机制,主要看消耗内存多少(先杀大户),mm/oom_kill.c的badness()给每个进程一个oom_score,一般取决于:
驻留内存、pagetable和swap的使用;
oom_score_adj:oom_score会加上oom_score_adj这个值;
oom_adj:-17~15的系数调整
关掉交换内存分区:
sudo swapoff -a
sudo sh -c ‘echo 1 \> /proc/sys/vm/overconmit_memory’
git grep overcommit_memory
dmesg //查看oom进程的oom_score
查看chrome进程的oom_score
pidof chrome
cd /proc/28508/
cat oom_score
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1IA0BSXz-1598498836065)(media/60320ab44bb614699daf3e9d8987662f.png)]](https://i-blog.csdnimg.cn/blog_migrate/a100df0a7510632e2e6b6be971dc013a.png#pic_center)
由上图可知,
1将oom_adj值设置越大,oom_score越大,进程越容易被杀掉;
2.将oom_adj设置更大时,普通权限就可以;要将oom_adj设置更小,需要root权限;
即设置自身更容易死掉,自我牺牲是很容易的,设置自己不容易死,是索取,需要超级权限才可以;
4 Android进程生命周期与OOM
android进程的生命周期靠OOM来驱动;
android手机多个进程间切换时,会动态设置相应oom_adj,调低前台进程的oom_adj,调高后台进程的oom_adj,当内存不够时,优先杀后台进程。所以内存足够大,更容易平滑切换。
对于简单的嵌入式系统,可以设置当oom时,是否panic
cd /proc/sys/vm
cat panic_on_oom
mm/oom_kill.c oom_badness()函数不停调整进程oom_score值;
总结一个典型的ARM32位,Linux系统,内存分布简图;
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wnKzoP6Y-1598498836066)(media/af35bc71b0929f7331e05f24c4465db6.png)]](https://i-blog.csdnimg.cn/blog_migrate/47ec990bf90d861b9b5d8290c2913d76.png#pic_center)
ARM32位内核空间3G-16M~4G
3G-16M~3G-2M用来映射KO
3G-2M~3G:可以用kmap申请高端内存,用kmap,建立一个映射,临时访问页,访问完后kunmap掉;
进程的写时拷贝技术,mm/memory.c/cow_user_page用到kmap映射;
其他练习:
1.看/proc/slabinfo,运行slabtop
运行mallopt.c程序:mallopt等
运行一个很耗费内存的程序,观察oom memory
通过oom_adj调整firefox的oom_score
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)