linux内核那些事之 VMA Gap
rb_subtree_gap
kernel为了提高对VMA 查找、插入速度,采用了很多技术,例如VMA采用RB tree和 双向链表两种组织形式,另外对VMA结构对cache line对齐从而提高cache命中率等。由于内存在实际使用过程中,不断的申请释放,使得原来成片的连续内存空间变成了一段段不连续空间,每个VMA都存在一定空闲空间即gap:
在查找GAP时,一般为了知道GAP大小,需要知道前一个VMA prev的end地址和当前VMA的start地址即GAP=vma->start-prev->end.此时需要知道两个VMA。linux内核中为了加速查找适合的GAP,对此进行了优化 vma结构中增加了 rb_subtree_gap 字段,用于记录当前vma 的前面有多少空闲空间:
struct vm_area_struct {.... .../** Largest free memory gap in bytes to the left of this VMA.* Either between this VMA and vma->vm_prev, or between one of the* VMAs below us in the VMA rbtree and its ->vm_prev. This helps* get_unmapped_area find a free area of the right size.*/unsigned long rb_subtree_gap;... ...
} __randomize_layout;
rb_subtree_gap:用于记录该VMA左侧有多少空间,即vma->prev 和 vma之前的空闲空间,以便能够快速查找当前vma前面空闲空间是否符合要求。
vma subtree Gap 计算
vma GAP计算相对比较简单,函数为vma_compute_subtree_gap():
static inline unsigned long vma_compute_gap(struct vm_area_struct *vma)
{unsigned long gap, prev_end;/** Note: in the rare case of a VM_GROWSDOWN above a VM_GROWSUP, we* allow two stack_guard_gaps between them here, and when choosing* an unmapped area; whereas when expanding we only require one.* That's a little inconsistent, but keeps the code here simpler.*/gap = vm_start_gap(vma);if (vma->vm_prev) {prev_end = vm_end_gap(vma->vm_prev);if (gap > prev_end)gap -= prev_end;elsegap = 0;}return gap;
}
计算相对比较简单:
- vm_start_gap: 获取当前vma 的起始地址;
- 如果vma->vm_prev 为空,则意味着前面空间都是空闲空间
- 如果vma->vm_prev为非空,获取vma指向的前一个vma即vma->vm_prev的结束地址
- 如果prev_end 大于等于 vma的起始地址(严格来说不可能大于 只能等于),则说明没有空闲空间返回为0
- 如果prev 小于gap,则说明有空闲时间。
vm_start_gap
vm_start_gap()为获取vma的起始地址进行了封装:
static inline unsigned long vm_start_gap(struct vm_area_struct *vma)
{unsigned long vm_start = vma->vm_start;if (vma->vm_flags & VM_GROWSDOWN) {vm_start -= stack_guard_gap;if (vm_start > vma->vm_start)vm_start = 0;}return vm_start;
}
如果当前vma 设置了VM_GROWSDOWN, 则说明该vma设置了保护页,需要将在vma的起始地址vma_start 减去stack_guard_gap即将保护页空间给预留出来
vm_end_gap
vm_end_gap即获取当前vma的 end结束地址:
static inline unsigned long vm_end_gap(struct vm_area_struct *vma)
{unsigned long vm_end = vma->vm_end;if (vma->vm_flags & VM_GROWSUP) {vm_end += stack_guard_gap;if (vm_end < vma->vm_end)vm_end = -PAGE_SIZE;}return vm_end;
}
如果vma 设置VM_GROWSUP,则说明vma需要在其右边即后面设置预留保护页,所以vm_end 要加上stack_guard_gap.
vma gap update
vma gap 一般在创建一个新的vma时,将新的vma插入到rb tree和链表后,需要更新当前vma插入节点时next vma的gap:
void __vma_link_rb(struct mm_struct *mm, struct vm_area_struct *vma,struct rb_node **rb_link, struct rb_node *rb_parent)
{/* Update tracking information for the gap following the new vma. */if (vma->vm_next)vma_gap_update(vma->vm_next);elsemm->highest_vm_end = vm_end_gap(vma);/** vma->vm_prev wasn't known when we followed the rbtree to find the* correct insertion point for that vma. As a result, we could not* update the vma vm_rb parents rb_subtree_gap values on the way down.* So, we first insert the vma with a zero rb_subtree_gap value* (to be consistent with what we did on the way down), and then* immediately update the gap to the correct value. Finally we* rebalance the rbtree after all augmented values have been set.*/rb_link_node(&vma->vm_rb, rb_parent, rb_link);vma->rb_subtree_gap = 0;vma_gap_update(vma);vma_rb_insert(vma, &mm->mm_rb);
}
调用 vma_gap_update()函数进行更新:
vma_gap_update
vma_gap_update是更新当前vma gap大小:
/** Update augmented rbtree rb_subtree_gap values after vma->vm_start or* vma->vm_prev->vm_end values changed, without modifying the vma's position* in the rbtree.*/
static void vma_gap_update(struct vm_area_struct *vma)
{/** As it turns out, RB_DECLARE_CALLBACKS_MAX() already created* a callback function that does exactly what we want.*/vma_gap_callbacks_propagate(&vma->vm_rb, NULL);
}
vma_gap_callbacks_propagate()调用的回调使用的是RB_DECLARE_CALLBACKS_MAX 定义:
RB_DECLARE_CALLBACKS_MAX
该宏为一个回调函数定义的宏:
#define RB_DECLARE_CALLBACKS_MAX(RBSTATIC, RBNAME, RBSTRUCT, RBFIELD, \RBTYPE, RBAUGMENTED, RBCOMPUTE) \
static inline bool RBNAME ## _compute_max(RBSTRUCT *node, bool exit) \
{ \RBSTRUCT *child; \RBTYPE max = RBCOMPUTE(node); \if (node->RBFIELD.rb_left) { \child = rb_entry(node->RBFIELD.rb_left, RBSTRUCT, RBFIELD); \if (child->RBAUGMENTED > max) \max = child->RBAUGMENTED; \} \if (node->RBFIELD.rb_right) { \child = rb_entry(node->RBFIELD.rb_right, RBSTRUCT, RBFIELD); \if (child->RBAUGMENTED > max) \max = child->RBAUGMENTED; \} \if (exit && node->RBAUGMENTED == max) \return true; \node->RBAUGMENTED = max; \return false; \
} \
RB_DECLARE_CALLBACKS(RBSTATIC, RBNAME, \RBSTRUCT, RBFIELD, RBAUGMENTED, RBNAME ## _compute_max)
该宏定义为 RBNAME ## _compute_max 函数名,而vma gap回调定义为:
RB_DECLARE_CALLBACKS_MAX(static, vma_gap_callbacks,struct vm_area_struct, vm_rb,unsigned long, rb_subtree_gap, vma_compute_gap)
通过展开可知定义一个vma_gap_callbacks_compute_max函数,而RB_DECLARE_CALLBACKS_MAX中 使用RB_DECLARE_CALLBACK进一步定义回调函数:
#define RB_DECLARE_CALLBACKS(RBSTATIC, RBNAME, \RBSTRUCT, RBFIELD, RBAUGMENTED, RBCOMPUTE) \
static inline void \
RBNAME ## _propagate(struct rb_node *rb, struct rb_node *stop) \
{ \while (rb != stop) { \RBSTRUCT *node = rb_entry(rb, RBSTRUCT, RBFIELD); \if (RBCOMPUTE(node, true)) \break; \rb = rb_parent(&node->RBFIELD); \} \
}
通过RBNAME##_propagate宏定义可知进一步展开为vma_gap_callbacks_propagate函数,该函数定义展开找到。
linux内核那些事之 VMA Gap相关推荐
- linux内核那些事之VMA常用操作
内核对vm_area_struct 数据结构相关操作进行了一系列封装,方面进行后续操作,vma的操作实现大部分位于mm\mmap.c文件中 find_vma() find_vma()是内核中经常需要查 ...
- linux内核那些事之mmap_region流程梳理
承接<linux内核那些事之mmap>,mmap_region()是申请一个用户进程虚拟空间 并根据匿名映射或者文件映射做出相应动作,是实现mmap关键函数,趁这几天有空闲时间 整理下mm ...
- linux内核那些事之buddy(慢速申请内存__alloc_pages_slowpath)(5)
内核提供__alloc_pages_nodemask接口申请物理内存主要分为两个部分:快速申请物理内存get_page_from_freelist(linux内核那些事之buddy(快速分配get_p ...
- linux内核那些事之buddy(anti-fragment机制)(4)
程序运行过程中,有些内存是短暂的驻留 用完一段时间之后就可以将内存释放以供后面再次使用,但是有些内存一旦申请之后,会长期使用而得不到释放.长久运行有可能造成碎片.以<professional l ...
- linux内核那些事之buddy
buddy算法是内核中比较古老的一个模块,很好的解决了相邻物理内存碎片的问题即"内碎片问题",同时有兼顾内存申请和释放效率问题,内核从引入该算法之后一直都能够在各种设备上完好运行, ...
- linux内核那些事之pg_data_t、zone结构初始化
free_area_init 继续接着<linux内核那些事之ZONE>,分析内核物理内存初始化过程,zone_sizes_init()在开始阶段主要负责对各个类型zone 大小进行计算, ...
- linux内核那些事之Sparse vmemmap
<inux内核那些事之物理内存模型之SPARCE(3)>中指出在传统的sparse 内存模型中,每个mem_section都有一个属于自己的section_mem_map,如下图所示: 而 ...
- linux内核那些事之buddy(anti-fragment机制-steal page)(5)
继<linux内核那些事之buddy(anti-fragment机制)(4)>,在同一个zone内指定的migrate type中没有足够内存,会启动fallback机制,从fallbac ...
- linux内核那些事之用户空间管理
内核主要数据结构 linux内核将用户空间抽象成struct vm_area_struct进行管理,每申请以个用户空间在内核中都会抽象成对应的vm_are_struct进行管理,同时为了区别不同进程的 ...
最新文章
- 解决nginx 502 bad gateway--团队的力量
- 5G会用什么样的语音通信方案?
- Appium环境搭建简介
- STM32 基础系列教程 14 - IIC
- 竞价账户整改技巧-小脑袋竞价软件
- 并查集(图论) LA 3644 X-Plosives
- 集群(二)——LVS-DR-Keepalived
- xvhfeng的工作回忆总结(第二年)阅读手记
- jsp在mysql中删除数据_如何在jsp页面中删除数据库中的数据
- 网页速度很慢优化方案:如何提高网页加载速度,提升网站加载速度
- Entity Framework Core 7.0 未来规划
- 网页脚本基本java语法_JSP学习(一)JSP基础语法
- 迅雷精简版 4.0.0 Mac中文版
- Visual C++ 2010创建Ribbon界面
- 【方差分析】之matlab求解
- Docker镜像分层原理-联合文件系统(UnionFS)
- 如何设置excel表格表头冻结_excel怎么冻结窗口固定表头_excel冻结窗口固定表头详细教程 - 系统家园...
- 纵向时间线html,51个css时间轴
- 稀土配合物Ln(DBM)3(Cz-PBM)|Tb(DBM)3(Cz-PBM)|Gd(DBM)3(Cz-PBM)|Ir(L)2(DBM-Ox)Ir(L)2(DBM-Cz)qiyue
- oracle用户常见job权限不足,JOB调用的权限问题
热门文章
- PowerDesigner反向工程 mysql
- Java中主线程如何捕获子线程抛出 ...
- 再不懂ZooKeeper,就安安心心把这篇文章看完
- 学习 python logging(1): 基本用法
- jsondataobjects
- Python(pycharm)在windows下路径 ( ' / ' 与' \ ' )的问题
- 在HermesJMS中创建ActiveMQ Session
- Mysql学习笔记(六)增删改查
- 12款最佳的 WordPress 语法高亮插件推荐
- cisco初级随堂笔记2