【原创】(十二)Linux内存管理之vmap与vmalloc
背景
Read the fucking source code!
--By 鲁迅A picture is worth a thousand words.
--By 高尔基
说明:
- Kernel版本:4.14
- ARM64处理器,Contex-A53,双核
- 使用工具:Source Insight 3.5, Visio
1. 概述
在之前的系列文章中,分析到了Buddy System
的页框分配,Slub分配器
的小块内存对象分配,这些分配的地址都是物理内存连续的。当内存碎片后,连续物理内存的分配就会变得困难,可以使用vmap
机制,将不连续的物理内存页框映射到连续的虚拟地址空间中。vmalloc
的分配就是基于这个机制来实现的。
还记得下边这张图吗?
vmap/vmalloc
的区域就是在VMALLOC_START ~ VMALLOC_END
之间。
开启探索之旅吧。
2. 数据结构
2.1 vmap_area/vm_struct
这两个数据结构比较简单,直接上代码:
struct vm_struct {
struct vm_struct *next;
void *addr;
unsigned long size;
unsigned long flags;
struct page **pages;
unsigned int nr_pages;
phys_addr_t phys_addr;
const void *caller;
};
struct vmap_area {
unsigned long va_start;
unsigned long va_end;
unsigned long flags;
struct rb_node rb_node; /* address sorted rbtree */
struct list_head list; /* address sorted list */
struct llist_node purge_list; /* "lazy purge" list */
struct vm_struct *vm;
struct rcu_head rcu_head;
};
struct vmap_area
用于描述一段虚拟地址的区域,从结构体中va_start/va_end
也能看出来。同时该结构体会通过rb_node
挂在红黑树上,通过list
挂在链表上。
struct vmap_area
中vm
字段是struct vm_struct
结构,用于管理虚拟地址和物理页之间的映射关系,可以将struct vm_struct
构成一个链表,维护多段映射。
关系如下图:
2.2 红黑树
红黑树,本质上是一种二叉查找树,它在二叉查找树的基础上增加了着色相关的性质,提升了红黑树在查找,插入,删除时的效率。在红黑树中,节点已经进行排序,对于每个节点,左侧的的元素都在节点之前,右侧的元素都在节点之后。
红黑树必须满足以下四条规则:
- 每个节点不是红就是黑;
- 红黑树的根必须是黑;
- 红节点的子节点必须为黑;
- 从节点到子节点的每个路径都包含相同数量的黑节点,统计黑节点个数时,空指针也算黑节点;
定义如下:
struct rb_node {
unsigned long __rb_parent_color;
struct rb_node *rb_right;
struct rb_node *rb_left;
} __attribute__((aligned(sizeof(long))));
/* The alignment might seem pointless, but allegedly CRIS needs it */
由于内核会频繁的进行vmap_area
的查找,红黑树的引入就是为了解决当查找数量非常多时效率低下的问题,在红黑树中,搜索元素,插入,删除等操作,都会变得非常高效。至于红黑树的算法操作,本文就不再深入分析,知道它的用途即可。
3. vmap/vunmap分析
3.1 vmap
vmap
函数,完成的工作是,在vmalloc
虚拟地址空间中找到一个空闲区域,然后将page页面数组
对应的物理内存映射到该区域,最终返回映射的虚拟起始地址。
整体流程如下:
操作流程比较简单,来一个样例分析,就清晰明了了:
vmap
调用中,关键函数为alloc_vmap_area
,它先通过vmap_area_root
二叉树来查找第一个区域first vm_area
,然后根据这个first vm_area
去查找vmap_area_list
链表中满足大小的空间区域。
在alloc_vmap_area
函数中,有几个全局的变量:
static struct rb_node *free_vmap_cache;
static unsigned long cached_hole_size;
static unsigned long cached_vstart;
static unsigned long cached_align;
用于缓存上一次分配成功的vmap_area
,其中cached_hole_size
用于记录缓存vmap_area
对应区域之前的空洞的大小。缓存机制当然也是为了提高分配的效率。
3.2 vunmap
vunmap
执行的是跟vmap
相反的过程:从vmap_area_root/vmap_area_list
中查找vmap_area
区域,取消页表映射,再从vmap_area_root/vmap_area_list
中删除掉vmap_area
,页面返还给伙伴系统等。由于映射关系有改动,因此还需要进行TLB的刷新,频繁的TLB刷新会降低性能,因此将其延迟进行处理,因此称为lazy tlb
。
来看看逆过程的流程:
4. vmalloc/vfree分析
4.1 vmalloc
vmalloc
用于分配一个大的连续虚拟地址空间,该空间在物理上不连续的,因此也就不能用作DMA缓冲区。vmalloc
分配的线性地址区域,在文章开头的图片中也描述了:VMALLOC_START ~ VMALLOC_END
。
直接分析调用流程:
从过程中可以看出,vmalloc
和vmap
的操作,大部分的逻辑操作是一样的,比如从VMALLOC_START ~ VMALLOC_END
区域之间查找并分配vmap_area
, 比如对虚拟地址和物理页框进行映射关系的建立。不同之处,在于vmap
建立映射时,page
是函数传入进来的,而vmalloc
是通过调用alloc_page
接口向Buddy System申请分配的。
vmalloc VS kmalloc
到现在,我们应该能清楚vmalloc
和kmalloc
的差异了吧,kmalloc
会根据申请的大小来选择基于slub分配器
或者基于Buddy System
来申请连续的物理内存。而vmalloc
则是通过alloc_page
申请order = 0
的页面,再映射到连续的虚拟空间中,物理地址不连续,此外vmalloc
可以休眠,不应在中断处理程序中使用。
与vmalloc
相比,kmalloc
使用ZONE_DMA和ZONE_NORMAL
空间,性能更快,缺点是连续物理内存空间的分配容易带来碎片问题,让碎片的管理变得困难。
4.2 vfree
直接上代码:
void vfree(const void *addr)
{
BUG_ON(in_nmi());
kmemleak_free(addr);
if (!addr)
return;
if (unlikely(in_interrupt()))
__vfree_deferred(addr);
else
__vunmap(addr, 1);
}
如果在中断上下文中,则推迟释放,否则直接调用__vunmap
,所以它的逻辑基本和vunmap
一致,不再赘述了。
【原创】(十二)Linux内存管理之vmap与vmalloc的更多相关文章
- linux内存管理之malloc、vmalloc、kmalloc的区别
kmalloc kzalloc vmalloc malloc 和get_free_page()的区别 一.简述 1. kmalloc申请的是较小的连续的物理内存,虚拟地址上也是连续的.kmalloc和 ...
- 《Linux内核设计与实现》读书笔记(十二)- 内存管理【转】
转自:http://www.cnblogs.com/wang_yb/archive/2013/05/23/3095907.html 内核的内存使用不像用户空间那样随意,内核的内存出现错误时也只有靠自己 ...
- MySQL 调优基础(二) Linux内存管理
进程的运行,必须使用内存.下图是Linux中进程中的内存的分布图: 其中最重要的 heap segment 和 stack segment.其它内存段基本是大小固定的.注意stack是向低地址增长的, ...
- 【原创】(十)Linux内存管理 - zoned page frame allocator - 5
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- Linux内存管理 (6)vmalloc
专题:Linux内存管理专题 关键词:vmalloc.页对齐.虚拟地址连续.物理不连续 至此,已经介绍了集中内核中内存分配函数,在开始简单做个对比总结Linux中常用内存分配函数的异同点,然后重点介绍 ...
- 启动期间的内存管理之bootmem_init初始化内存管理–Linux内存管理(十二)
1. 启动过程中的内存初始化 首先我们来看看start_kernel是如何初始化系统的, start_kerne定义在init/main.c?v=4.7, line 479 其代码很复杂, 我们只截取 ...
- 十问 Linux 虚拟内存管理 (glibc) (二)
版权声明:本文由陈福荣原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/184 来源:腾云阁 https://www.qclo ...
- 伙伴系统之避免碎片--Linux内存管理(十六)
1 前景提要 1.1 碎片化问题 分页与分段 页是信息的物理单位, 分页是为了实现非连续分配, 以便解决内存碎片问题, 或者说分页是由于系统管理的需要. 段是信息的逻辑单位,它含有一组意义相对完整的信 ...
- Linux内存管理(二)
Linux内存管理之二:Linux在X86上的虚拟内存管理 本文档来自网络,并稍有改动. 前言 Linux支持很多硬件运行平台,常用的有:Intel X86,Alpha,Sparc等.对于不能够通用的 ...
随机推荐
- SpringBoot整合Swagger2(Demo示例)
写在前面 由于公司项目采用前后端分离,维护接口文档基本上是必不可少的工作.一个理想的状态是设计好后,接口文档发给前端和后端,大伙按照既定的规则各自开发,开发好了对接上了就可以上线了.当然这是一种非常理 ...
- SpringBoot整合MybatisPlus3.X之自定义Mapper(十)
pom.xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId& ...
- PHP 输出XML字符串
<?php echo htmlentities( $xml_string);?>
- 数字麦克风PDM信号采集与STM32 I2S接口应用(三)
本文是数字麦克风笔记文章的数据处理篇. 读取数字麦克风的信号,需要嵌入式驱动和PC应用的结合,驱动负责信号采集,应用代码负责声音分析. 一般而言,在完成特征分析和实验之后,把优化过的代码固化到嵌入式端 ...
- 开根号 HYSBZ - 3211
区间修改+区间查询(线段树板子题) 另外因为1e9内的数开5次根号必定为1或0,所以我们可以提前打表i<=sqrt[1e9], s[i]=sqrt(i).这样每次改值不必再调用系统的sqrt: ...
- python(可迭代对象,迭代器,生成器及send方法详解)
一.可迭代对象 对象必须提供一个__iter__()方法,如果有,那么就是可迭代对象, 像列表,元祖,字典等都是可迭代对象可使用isinstance(obj,Iterable)方法判断 from co ...
- android 7.0 pm install 失败问题
这两天,把app内置成系统文件或者系统签名取得系统权限,发现使用pm install path在低版本上可行,在高版本就安装失败 抛出 NullPointException,查看了一下源码,发现对包名 ...
- [考试反思]0922csp-s模拟测试50:谜朦
这辈子第5个rank1,是在长期状态低迷再度偶遇傻逼出题人然后总算在下午而不是晚上考了一场试 然后莫名其妙选对了头铁的题把其它暴力打满之后发现sdfz没有参加之后竞争减弱的综合结果. 说是在的其实这套 ...
- 基于Rancher搭建Kubernetes
基于Rancher搭建Kubernetes可以大大的简化安装的步骤,直接安装Kubernetes操作复杂并且容易出错. 转自 https://blog.csdn.net/u011142688/arti ...
- 数据结构--树链剖分准备之LCA
有关LCA的模板题 传送门 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和 ...