天天看点

【原创】(十二)Linux内存管理之vmap与vmalloc

<code>Read the fucking source code!</code> --By 鲁迅

<code>A picture is worth a thousand words.</code> --By 高尔基

说明:

Kernel版本:4.14

ARM64处理器,Contex-A53,双核

使用工具:Source Insight 3.5, Visio

在之前的系列文章中,分析到了<code>Buddy System</code>的页框分配,<code>Slub分配器</code>的小块内存对象分配,这些分配的地址都是物理内存连续的。当内存碎片后,连续物理内存的分配就会变得困难,可以使用<code>vmap</code>机制,将不连续的物理内存页框映射到连续的虚拟地址空间中。<code>vmalloc</code>的分配就是基于这个机制来实现的。

还记得下边这张图吗?

【原创】(十二)Linux内存管理之vmap与vmalloc

<code>vmap/vmalloc</code>的区域就是在<code>VMALLOC_START ~ VMALLOC_END</code>之间。

开启探索之旅吧。

这两个数据结构比较简单,直接上代码:

<code>struct vmap_area</code>用于描述一段虚拟地址的区域,从结构体中<code>va_start/va_end</code>也能看出来。同时该结构体会通过<code>rb_node</code>挂在红黑树上,通过<code>list</code>挂在链表上。

<code>struct vmap_area</code>中<code>vm</code>字段是<code>struct vm_struct</code>结构,用于管理虚拟地址和物理页之间的映射关系,可以将<code>struct vm_struct</code>构成一个链表,维护多段映射。

关系如下图:

【原创】(十二)Linux内存管理之vmap与vmalloc

红黑树,本质上是一种二叉查找树,它在二叉查找树的基础上增加了着色相关的性质,提升了红黑树在查找,插入,删除时的效率。在红黑树中,节点已经进行排序,对于每个节点,左侧的的元素都在节点之前,右侧的元素都在节点之后。

红黑树必须满足以下四条规则:

每个节点不是红就是黑;

红黑树的根必须是黑;

红节点的子节点必须为黑;

从节点到子节点的每个路径都包含相同数量的黑节点,统计黑节点个数时,空指针也算黑节点;

定义如下:

【原创】(十二)Linux内存管理之vmap与vmalloc

由于内核会频繁的进行<code>vmap_area</code>的查找,红黑树的引入就是为了解决当查找数量非常多时效率低下的问题,在红黑树中,搜索元素,插入,删除等操作,都会变得非常高效。至于红黑树的算法操作,本文就不再深入分析,知道它的用途即可。

<code>vmap</code>函数,完成的工作是,在<code>vmalloc</code>虚拟地址空间中找到一个空闲区域,然后将<code>page页面数组</code>对应的物理内存映射到该区域,最终返回映射的虚拟起始地址。

整体流程如下:

【原创】(十二)Linux内存管理之vmap与vmalloc

操作流程比较简单,来一个样例分析,就清晰明了了:

【原创】(十二)Linux内存管理之vmap与vmalloc

<code>vmap</code>调用中,关键函数为<code>alloc_vmap_area</code>,它先通过<code>vmap_area_root</code>二叉树来查找第一个区域<code>first vm_area</code>,然后根据这个<code>first vm_area</code>去查找<code>vmap_area_list</code>链表中满足大小的空间区域。

【原创】(十二)Linux内存管理之vmap与vmalloc

在<code>alloc_vmap_area</code>函数中,有几个全局的变量:

用于缓存上一次分配成功的<code>vmap_area</code>,其中<code>cached_hole_size</code>用于记录缓存<code>vmap_area</code>对应区域之前的空洞的大小。缓存机制当然也是为了提高分配的效率。

<code>vunmap</code>执行的是跟<code>vmap</code>相反的过程:从<code>vmap_area_root/vmap_area_list</code>中查找<code>vmap_area</code>区域,取消页表映射,再从<code>vmap_area_root/vmap_area_list</code>中删除掉<code>vmap_area</code>,页面返还给伙伴系统等。由于映射关系有改动,因此还需要进行TLB的刷新,频繁的TLB刷新会降低性能,因此将其延迟进行处理,因此称为<code>lazy tlb</code>。

来看看逆过程的流程:

【原创】(十二)Linux内存管理之vmap与vmalloc

<code>vmalloc</code>用于分配一个大的连续虚拟地址空间,该空间在物理上不连续的,因此也就不能用作DMA缓冲区。<code>vmalloc</code>分配的线性地址区域,在文章开头的图片中也描述了:<code>VMALLOC_START ~ VMALLOC_END</code>。

直接分析调用流程:

【原创】(十二)Linux内存管理之vmap与vmalloc

从过程中可以看出,<code>vmalloc</code>和<code>vmap</code>的操作,大部分的逻辑操作是一样的,比如从<code>VMALLOC_START ~ VMALLOC_END</code>区域之间查找并分配<code>vmap_area</code>, 比如对虚拟地址和物理页框进行映射关系的建立。不同之处,在于<code>vmap</code>建立映射时,<code>page</code>是函数传入进来的,而<code>vmalloc</code>是通过调用<code>alloc_page</code>接口向Buddy System申请分配的。

<code>vmalloc VS kmalloc</code>

到现在,我们应该能清楚<code>vmalloc</code>和<code>kmalloc</code>的差异了吧,<code>kmalloc</code>会根据申请的大小来选择基于<code>slub分配器</code>或者基于<code>Buddy System</code>来申请连续的物理内存。而<code>vmalloc</code>则是通过<code>alloc_page</code>申请<code>order = 0</code>的页面,再映射到连续的虚拟空间中,物理地址不连续,此外<code>vmalloc</code>可以休眠,不应在中断处理程序中使用。

与<code>vmalloc</code>相比,<code>kmalloc</code>使用<code>ZONE_DMA和ZONE_NORMAL</code>空间,性能更快,缺点是连续物理内存空间的分配容易带来碎片问题,让碎片的管理变得困难。

直接上代码:

如果在中断上下文中,则推迟释放,否则直接调用<code>__vunmap</code>,所以它的逻辑基本和<code>vunmap</code>一致,不再赘述了。

【原创】(十二)Linux内存管理之vmap与vmalloc

作者:LoyenWang

出处:https://www.cnblogs.com/LoyenWang/

公众号:<b>LoyenWang</b>

版权:本文版权归作者和博客园共有

转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任

继续阅读