linux 内存管理 二

疫情憋在家,继续写下笔记,Buddy 算法把空闲页面管理起来,一个已经被占用的页面,其他人肯定不能申请到的,这些页面都是4k 8k 16k .... ,无论是在内核还是在应用,有时候都要申请很小的内存。比如说我们在内核中需要申请16Bytes,Buddy 还是申请4K的内存,这样就很乱费,所以在内核里面会进行内存的二次管理,在Buddy中申请的内存进行切割,内核中这种机制叫做slab。

Slab 原理:

Slab 会把从buddy分配的内存分配为很多小的object,当所有小的object分配完了,再次从buddy 申请页拆分,类似于现在疫情期间,妈妈去超市买一个大块的牛肉,分成很多小块,每天吃一块,当所有的牛肉吃完了,就再次去超市买一大块。Slab ,slub, slob 都是slab的算法,这是这种机制的三种不同的实现。

Slab主要分成两类,一类是内核中常用的数据结构,内核会给他们做一个slab,比如下面图中的TCPv6 的slab ,内核中经常要分配使用,所以这个专门用slab专门用于TCPv6的内存分配。针对第二幅图中kmalloc16 kmalloc-8 等体现出来针对的常规的小内存神奇,可以看到没法直接申请48个字节,所以如果我们申请48Bytes,实际申请的也是64Bytes。Slabinfo的头部显示了slab的详细信息。

linux 内存管理 二

linux 内存管理 二

不过slab指针对内核空间的,跟应用是没有任何关系的,用户空间调用的malloc是调用libc的

linux 内存管理 二

Slab的内存来自于buddy,他们在算法级别上是对等的,buddy是把你的内存条当作一个池子管理,slab 是把从buddy申请的内存当作池子管理,他是一个二级管理,针对的是内存空间的kmalloc。不仅仅内核空间有个二级管理器,用户空间同样存在一个二级管理器,当你在用户空间调用malloc ,malloc不是系统调用,他是从libc中获取的,所以应用的内存申请和释放,并不一定对应内核的内存申请和释放,libc不一定会释放给内核。

linux 内存管理 二

所以应用程序开发,特别是针对RT程序,有一些技巧,下划线的配置告诉C库,无论应用释放多少内存都不需要还给内核。这个接口是设置c库释放给内核的阀值,-1 就是永远不还。所以不管这个应用程序申请和释放内存,都是在用户态进行的,所以他的实时性就会好。

linux 内存管理 二

下面是内存映射和申请的描述,不管是低端内核,还是高端内存都有可能被kmalloc vmalloc, 用户空间申请走。 被映射不等于被申请,完全存在两个虚拟地址对应一个物理地址。用户空间和vmalloc 申请跟kmalloc申请的区别是,需要更改相应的页表,kmalloc 申请的不需要改页表,因为开机已经一一映射了

linux 内存管理 二

下面这张图更详细描述他们之间的关系,这个是理解linux 内存非常重要的概念

linux 内存管理 二

Vmalloc 这个映射去,除了使用vmalloc 映射外,所有的寄存器通过ioremap也是映射到这里,vmalloc 申请的内存虚拟连续,物理不一定连续,kmalloc申请的内存物理和虚拟都是连续的。

linux 内存管理 二

用户空间申请的内存的时候都采用lazy的机制,比如用户空间申请100M空间返回的时候,其实一页都没有拿到。内核欺骗用户,这100M的虚拟地址都指向同一物理地址0页,并且页表中标记为只读。只有当你一页一页写的时候,每写一页就发生page fault,内核再给应用申请对应的页,更改页表为新申请的内存,并且为可读写。

linux 内存管理 二

VSS 是你的虚拟地址空间,RSS 是真正在内存里面驻留的内存,当你malloc 100M时,VSS是100M ,这个时候RSS并没有,只有你一页页的写,RSS 才会一页页增加。所以page fault 是应用程序真正拿到内存的一个触发源,所以完全可能出现一种情况,是后面真正写的时候,内存不够了,这时候linux就会启动著名的oom 机制,oom机制会找到一个最该杀掉的内存kill掉,从而腾出内存使用。

Linux 会对每个进程进行oom socre打分,把最高分数的优先杀掉,一般内存消耗越多,分数会越高,也会根据不同用户有差别,比如root用户会减去30,也可以通过oom_score_adj 进行调整,打分因子如下:

linux 内存管理 二

linux 内存管理 二

Android 整个生命周期的管理完全是靠OOM的,Android 是前台执行时,内存不够才会去杀后台进程,这个过程也是通过调高oom_adj 的值来实现的。但是对于一个嵌入式系统,内存是预期的,所以出现oom是有问题的,所以对于嵌入式系统一般会使能一个参数,/proc/sys/vm/panic_on_oom ,一旦系统出现内存耗尽,就会让内核崩溃,系统重启。