内存管理二

   本文从4个方面进行说明:

1、  Slab的基本原理以及它的文件接口说明

2、  kmalloc、vmalloc、malloc比较

3、  OOM是什么,为什么,怎么做

4、  FAQ:群里经常问到的,也是比较容易误解的问题

slab的基本原理以及它的文件接口说明

         在第一节课中,我们了解到,Linux的最底层,由Buddy算法管理着所有的空闲页面,最小单位是2的0次方页,就是1页,4K,但是很多时候,我们为一个结构分配空间,也只需要几十个字节,按页分配无疑是浪费空间;另外,当我们频繁的分配和释放一个结构,我们希望在释放的时候,这部分内存不要立刻还给Buddy,而是提供一种类似C库的管理机制,在下一次在分配的时候还可以拿到同一块内存且保留着基本的数据结构。基于上面两点,Slab应运而生。总结一下,Slab主要提供以下两个功能:

A. 对从Buddy拿到的内存进行二次管理,以更小的单位进行分配和回收(注意,是回收而不是释放),防止了空间的浪费。

B. 让频繁使用的对象尽量分配在同一块内存区间并保留基本数据结构,提高程序效率。

那么,Slab是如何工作的呢?

如果某个结构被频繁的使用,内核源码就可以针对这个结构建立一个或者多个Slab分区(姑且这么叫),每一个Slab分区从Buddy拿到1页或者多页内存,并把这些内存划分为多个等分的这个结构大小的小块内存,这些Slab分区只用于分配给这个结构,通常称这个结构为object,每一次当有该object的分配请求,内核就从对应的Slab分区拿一小块内存给object,这样就实现了在同一片内存区间为频繁使用的object分配内存。请看下图


黑框表示频繁使用的结构;红框表示slab分区,一个结构内核可能为它分配一个或多个Slab;每个Slab分区有可能包含多个page,被分隔开的多个红框表示Slab分区的多个pages;蓝框表示Slab分区为对应的Object划分的一个一个的小内存块。填充黄色的框表示active的object,灰色填充的框表示未active的object,如果整个Slab分区的所有蓝框都是灰色的,表示这个Slab分区是未active的。

Linux为用户提供了Slab的文件查看接口,和命令接口。

文件接口:/proc/slabinfo


上图所示为slabinfo文件的内容,第一行为表头:

Name:Object名字

Active_objs:已经激活的投入使用的object个数

Num_objs:为这个object分配的小内存块个数

Objsize:每一个内存块的大小

Objperslab:每一个Slab分区包含的object个数

Pagesperslab:每个Slab分区包含的page的个数

Active_slabs:已经激活的投入使用的Slab分区个数

Num_slabs:为这个object分配的Slab分区个数

         我在查看Slabinfo文件的时候,发现有的Num_objs为0,正常Active_objs为0是可以理解的,但是总的object数不应该为0啊,然后继续看,Active_slabs和Num_slabs也都为0,也就是说,这个时候内存还没有为这个结构分配Slab分区,一切就都解释的通了。

         另外还有一部分slabinfo的内容是这样的:


就是说,除了经常频繁使用的结构,内核为他们分配了slabs,还同时定义了一些特定大小的slabs供驱动使用。

命令接口:slabtop

直接运行slabtop命令(要加sudo,上面查看slabinfo文件同样),内容如下


有点类似top命令,按照使用内存的多少进行排序。

最后再说一句,slab只用于分配低端内存,所分配的内存也只会被映射到低端内存映射区,所以vmalloc跟slab一毛钱关系都没有。

kmalloc、vmalloc、malloc比较

         这部分内容牵连太多,也不好区分,直接上图:


如上图所示:

A. kmalloc函数是基于slab算法的,从物理内存的low mem获取内存,并线性映射到低端内存映射区(映射过程开机就已经完成了),由于是线性映射,物理地址和虚拟地址存在简单的转换关系(物理地址和虚拟地址的值只是相差了一个固定的偏移),所以使用kmalloc分配内存是十分高效的。

B. vmalloc函数分配内存的过程需要先通过alloc_pages函数获取内存(获取范围是整个内存条),然后在通过复杂的逻辑转换(注意,vmalloc并不是简单的线性映射,它获取的内存并不是连续的),把物理内存映射到vmalloc映射区。这个过程比较复杂,所以,如果只是使用vmalloc分配很小的内存空间是不合适的。

C. malloc是标准C库的函数,C库对申请的内存做二次管理,类似Slab。但是注意一点,当我们使用malloc函数申请一片内存时,实际上是从C库获取的内存,就是说,调用malloc返回后,系统未必给你一片真正的内存,分两种情况

a.  C库还持有足够的内存,那么malloc就可以直接分配到C库现有的内存

b.  C库没有足够的内存,malloc返回时,系统只是通过系统调用brk/mmap扩展了heap的大小,malloc以为自己拿到了全部内存,其实并没有,系统只是把要申请的内存大小的虚拟地址空间全部映射到同一块已经清零的物理内存,只有当我们实际要去写这片内存的时候,MMU发现这个物理页面并没有被真正分配,于是产生一个page fault通知内核,内核才真正的分配内存并改写进程页表。

D. 另外有一点值得一提,vmalloc映射区,除了vmalloc函数分配的内存会映射在该区域,设备的寄存器也同样会通过ioremap映射到该区域。

E.  根据上面要点C的描述,在编写实时程序时,我们可以通过下面这种方式,减少系统内存的频繁分配,而是基于C库管理的内存。

#include <malloc.h>

#include <sys/mman.h>

#define SOMESIZE (100*1024*1024)   // 100MB

int main(int argc, char *argv[])
{
    unsigned char*buffer;
    int i;
 
    if(!mlockall(MCL_CURRENT | MCL_FUTURE))//锁定进程当前和将来所有的内存,禁止交换到swap
       mallopt(M_TRIM_THRESHOLD,-1UL);//设置C库释放内存的阀值为最大正整数
    mallopt(M_MMAP_MAX,0);
 
    buffer =malloc(SOMESIZE);
    if (!buffer)
       exit(-1);
    /*
     * Touch each page in this piece of memory toget it
     * mapped into RAM
     */
    for (i = 0; i< SOMESIZE; i += 4 * 1024)//由于COW,确保内存被真实分配
       buffer[i]= 0;
    free(buffer);
    /* <doyour RT-thing> */
    /* 接下来的所有内存分配动作都不是触发系统内存的真是分配,而是从C库获取
     *大大提高程序的效率,确保程序实时性。
 */
    while(1);
    return 0;
}


OOM是什么,为什么,怎么做

         什么是OOM:上面提到,当我们使用malloc分配内存时,系统并没有真正的分配内存,而是采用欺骗性的手段,拖延分配内存的时机,防止无谓的内存消耗,只有在我们写入的时候,产生page fault才会拿到真实的内存,且是写多少才分配多少,那么,问题来了,当我们通过malloc获取一片内存并成功返回,然后开始逐步使用内存,系统也逐步的分配真实的内存给进程,但这个过程中,另外一个耗内存的程序快速的拿走所有的内存,导致我的进程在逐步写入的过程发现刚刚说好给我的内存现在没有了,这种情况就是OOM,out of memory!

         在Linux系统,每一个进程都有一个oom score,这个数值越高,说明进程消耗的内存越多,在发生OOM的情况下,oom score越高的进程就越有可能被系统干掉,从而缓解系统的内存压力。我们可以通过/proc/pid/oom_score文件查看进程的oom score。

         那么有没有什么办法可以调整进程的oom score,就算这个进程比较耗内存,但是在OOM时候,这个进程仍然不会干掉。系统提供两个接口文件给用户:

/proc/pid/oom_ adj:可配置范围是-17到15,设置为15,oom score最大,最容易被干掉,设置-16,oom score最小,设置-17为禁止使用OOM杀死该进程。

/proc/pid/oom_score_adj:oom score会加上这个值,也可以设置负数,但如果负数的绝对值大于oom score,oom score最小为0。

FAQ

Q.kfree和free函数调用后,内存是否还给了Buddy?

A.kmalloc分配的内存是基于slab的,malloc分配的内存是基于C库的,slab和C库都会对内存进行二次管理,实际到底有没有被释放,只有Slab和C库他们自己知道。

Q.kmalloc,vmalloc,malloc他们从哪个zone申请物理内存,然后映射到那个映射区?

A.kmalloc从low mem获取物理内存,然后映射到内核空间的低端内存映射区,vmalloc和malloc都可以从整个内存条获取内存,vmalloc申请的内存映射到vmalloc映射区,malloc申请的内存映射到进程的用户空间。


猜你喜欢

转载自blog.csdn.net/woyimibayi/article/details/79219606