Linux内存管理复习总结

3/3/2017来源:C/C++教程人气:535

首先来看一张图:

这张图就是linux分配内存的大致流程。下面我来总结一下。

分页

首先Linux是基于内存管理采用分页机制,内核代码中它将所有段基址都设置为0,Linux采用这样的方法直接避过了分段机制。仅仅用分段来控制用户态和用户态的访问权限。

内核内存分配

内核对于大块内存的分配基于伙伴算法,用来解决外部碎片的问题。伙伴算法最低分配的单位是一页。在物理内存的每个Zone中都有一个free_area[]数组,分别存储2的幂的页面。伙伴算法就是比如说申请8个页面,然后首先去查找free_area[3]有没有可用的8个页面,如果没有就将上一级16个页面一分为二,一部分分配出来,另一部分重新加入到free_area数组对应8个页面的位置。当内存释放时,这两个8个页面又会重新合并。(这种互逆的操作将最大力度减小碎片的产生)。

伙伴算法是基于一页分配的,内核中对于远小于一页的字节级别内存,比如task_struct结构体,使用伙伴算法就会产生内部碎片。

为了解决内部碎片,Linux引入了slab机制。

slab机制

内核通常会对某些小对象频繁进行分配,slab分配器通过对类似对象大小的缓存提供这个功能。

slab分配器类似于一个全局的对象池。可以缓存不同大小的对象,最小一个缓存行大小,最大则是128k。slab分配器有三层结构,分别是per-CPU的缓存对象,CPU间共享的缓存对象,每个NUMA节点共享的slab页面。为了避免自旋锁的竞争,内核分配缓存对象优先从per-CPU数组中获取,并且获取数组最热的对象。因为最热的对象可能还在L1 cache中。如果per-CPU数组中没有对象分配,则回去CPU共享的shead数组中查找对象,如果还没有,则会去针对NUMA节点的slab三个链表中查找。这三个链表是slab满链表,slab部分满链表,slab空链表。分配对象从slab部分满链表中查找,如果部分满链表中所有对象都分配出去了,则会将它加入slab满链表之中。如果slab部分满和空链表都没有了,则会调用get_freepage()从伙伴系统分配2的幂的页面。

slab机制实际上就是kmalloc的底层实现。

如果内核需要分配不连续的内存,那么会调用vmalloc,vmalloc建立内核虚拟内存对物理存储器的页面映射,但由于vmalloc向伙伴系统要要内存不连续,所以相对kmalloc效率会低一些。vmalloc分配时使用__GFP_HIGHMEM标志,优先从高端内存获取物理页。

vmalloc是对内核虚拟内存的映射,和进程内存分配类似,因使用缺页中断的机制,下面会说到。

进程的内存分配

创建进程fork(),动态内存malloc()都涉及到进程的内存分配。操作系统对于进程的内存分配,以mmap为例。malloc对于大于128k的内存使用mmap分配,mmap底层调用do_mmap(),由于是匿名映射,传入file指针为NULL,然后会选择调用当前进程的current->mm->get_unmapped_ared,在进程的虚拟地址空间分配一个VMA区域,并将该新的VMA加入到mm_struct管理的VMA链表和红黑树中。此时,内核并不为进程分配实际的物理页面(页表项标记不在内存)。当用户进程第一次访问这片内存时,虚拟地址经过MMU转换,通过页目录查找到相应页表项,页表项中标志表明对应页不再内存中。则触发缺页中断。由于是匿名映射,内核在内存中找到一块牺牲页,将牺牲页换出内存,将内存中该页的位置清零,并修改相应页表项。然后重启引发缺页中断的指令,这时相应页面已经在内存中了,进程就可以正常使用内存了。

参考:

linux内核—–内存管理相关技术 linux内存管理总结之内存分配 Linux内存管理机制 buddy伙伴算法 linux内存管理–缺页异常处理 linux缺页异常剖析–用户空间