Linux内核之内存2: 内存的动态申请、释放的原理和细节
  TEZNKK3IfmPf 2023年11月13日 21 0

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库的内存,其他进程无法使用,共享代码段,数据段独立;

Linux内核之内存2: 内存的动态申请、释放的原理和细节

(1)slab原理:

就是从Buddy拿到一个/多个页,分成多个相同大小的块,比如进程控制块task_struct这种内核中常用的结构体,可以先从Buddy申请一个slab池,实际kmalloc分配的时候,直接从slab里分配。可以提高速度,也可以使内存得到充分使用,减少内存碎片;

每一个slab里的所有块,大小一定是相同的;

sudo cat

Linux内核之内存2: 内存的动态申请、释放的原理和细节

cat

Linux内核之内存2: 内存的动态申请、释放的原理和细节

slab也是一个内存泄漏的源头;

应用程序free内存,未必一定释放,可以通过mallopt设置free内存的触发阈值,触发阈值,free才真正在Buddy
释放;

(2) kmalloc / vmalloc详细过程

(1)kmalloc从低端内存区域分配,该区域是开机就一次性映射好;所以kmalloc申请,不需要做映射, 且在物理内存上是连续的。
phys_to_virt/virt_to_phys只是一个简单的内存线性偏移。

(2)vmalloc申请的虚拟内存,可以从普通物理内存空间分配,也可以从低端内存分配;
调用vmalloc申请,会有一个虚拟地址到物理地址映射过程。

ioremap映射的也是vmalloc虚拟地址,不过不用申请内存,ioremap映射的是寄存器;

Linux内核之内存2: 内存的动态申请、释放的原理和细节

映射跟分配是两回事,低端被映射之后,不一定被使用;

sudo cat /proc/vmallocinfo |grep

Linux内核之内存2: 内存的动态申请、释放的原理和细节

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).

在真正写的时候,才去分配,这是按需分配,或者惰性分配;

Linux内核之内存2: 内存的动态申请、释放的原理和细节

只申请了VMA,未实际拿到物理内存,此时叫VSS;拿到实际内存后是RSS(驻留内存);

Linux内核之内存2: 内存的动态申请、释放的原理和细节

属于按需分配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
dmesg

查看chrome进程的oom_score

pidof chrome
cd /proc/28508/
cat

Linux内核之内存2: 内存的动态申请、释放的原理和细节

由上图可知,

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

mm/oom_kill.c oom_badness()函数不停调整进程oom_score值;

总结一个典型的ARM32位,Linux系统,内存分布简图;

Linux内核之内存2: 内存的动态申请、释放的原理和细节

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

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月13日 0

暂无评论

推荐阅读
TEZNKK3IfmPf