Slob分配器的数据结构和分配逻辑
Slob分配器的数据结构和分配逻辑
我们知道OS提供很多机制保证内存的管理,而分配器则是空闲的内存以一定的数据结构组织起来,通过合适的算法进行分配;
slob(simple list of blocks)分配器,与slab、slub设计思路基本一致,而数据结构并不复杂,我们作为基础首先学习,后续拓展到slub和slab;
1. 数据结构
使用三个链表分别记录管理当前的freelist,依据其size不同进行划分:
- 0 ~ 256 Bytes,添加到small list中,后续分配即在此list中查询;
- 256 ~ 1024 Bytes,添加到medium list中,后续分配即在此list中查询;
- 1024 ~ PageSize,添加到large list中,后续分配即在此list中查询;
超过一个page大小的size直接通过buddy分配,不需要经过slob分配器;
#define SLOB_BREAK1 256
#define SLOB_BREAK2 1024
static LIST_HEAD(free_slob_small);
static LIST_HEAD(free_slob_medium);
static LIST_HEAD(free_slob_large);
1.1 slob_list链
1.1.1 slob_list 整体结构
我们已经知道slob分配器中创建了三条链表,其数据结构保持一致:
- slob_list是一个双向量表,每次节点插入在head之后;
- 其中每个node是list_head结构,实际填充为page中的lru结构体;
- 遍历slob_list时通过container_of 获取到page地址;
整体如下图:
具体将next和prev体现出来则是:
相关插入逻辑:
set_slob_page_free(sp, slob_list);//将申请到的page(sp)加入到slob_list中
static void set_slob_page_free(struct page *sp, struct list_head *list)
{list_add(&sp->lru, list);__SetPageSlobFree(sp);
}
1.1.2 slob_list 分配后移动
分配后移动链表头,构成lru的处理:
- 判断当前分配节点是否需要移动
- 当前分配节点为slob_list -> next的时候不需要移动
- 另外只有一个节点的时候不需要移动
- 将slob_list从slob_list中移除;
- 将slob_list插入到当前分配page的前序;
//每次分配后会修改slob_list的顺序:
prev = sp->lru.prev;
//prev即当前分配页的前序(比如在page2上分配,prev即page3,prev->next = page2)
//slob_list即链表头,其prev是page1,其next是page3
if (prev != slob_list->prev && slob_list->next != prev->next)list_move_tail(slob_list, prev->next);//将page2从链表中移除,插入到head->next
static inline void list_move_tail(struct list_head *list, struct list_head *head)
{__list_del(list->prev, list->next);//删除slob_listlist_add_tail(list, head);//将slob_list 插入到当前分配页的前序,即下次第一个遍历的位置为当前分配页
}
static inline void __list_del(struct list_head *prev, struct list_head *next)
{//将传入节点删除,即上述传入的slob_listnext->prev = prev;prev->next = next;
}
static inline void list_add_tail(struct list_head *entry, struct list_head *head)
{//将slob_list插入到page2和page3之间__list_add(entry, head->prev, head);//__list_add(slob_list, page2->prev, page2);
}
static inline void __list_add(struct list_head *entry, struct list_head *prev, struct list_head *next)
{next->prev = entry;entry->next = next;entry->prev = prev;prev->next = entry;
}
分步图示:
删除slob_list头
将slob_list头插入到当前分配页(page2)的前序
图片示意修改:
操作就是将当前使用的page放到slob_list的next位置,使得下次遍历时第一个访问(总是优先访问正在使用的部分)
1.1.3 page结构
仅截取slob中使用到的部分:
struct page {/* First double word block */unsigned long flags; .../* Second double word */union {pgoff_t index; /* Our offset within mapping. */void *freelist; /* sl[aou]b first free object *//* page_deferred_list().prev -- second tail page */};union {...struct {union {...unsigned int active; /* SLAB */struct { /* SLUB */unsigned inuse:16;unsigned objects:15;unsigned frozen:1;};int units; /* SLOB */.../* Third double word block */union {struct list_head lru; ...struct kmem_cache *slab_cache; /* SL[AU]B: Pointer to slab */
1.2 page中slob_block链
通过slob_list遍历可以找到空间足够的page(判断page->units),具体来看page中如何管理slob_block结构:
page中维护freelist会指向此page中第一个free的slob_block
其中对于只有一个block大小的空间,存储-offset的值,以这样的方式解决存储空间不足的问题:
即对于每一个free node,根据其size划分为两种case:
如果size为1,则存储-offset,这样则可以根据获取到的正负作为判断依据;
如果size大于1,则存储,size和offset
相关结构:
#if PAGE_SIZE <= (32767 * 2)
typedef s16 slobidx_t;
#else
typedef s32 slobidx_t;
#endifstruct slob_block {slobidx_t units;
};
typedef struct slob_block slob_t;
接口 | 功能 |
---|---|
set_slob | 记录当前slob_block的大小和偏移 |
slob_units(slob_t *s) | 返回对应节点的block大小 |
slob_next(slob_t *s) | 返回下一个节点的值,即通过当前位置添加offset |
slob_last(slob_t *s) | 确认当前slob是否为本页的最后一个block,即通过next地址是否在本页内判断 |
2. 分配与释放
在了解到其数据结构的情况下,分配与释放的逻辑就很明确了;
2.1 分配逻辑
如下图示演示了新分配4个units大小的变化:
code注释部分:
/** slob_alloc: entry point into the slob allocator.*/
static void *slob_alloc(size_t size, gfp_t gfp, int align, int node)
{struct page *sp;struct list_head *prev;struct list_head *slob_list;slob_t *b = NULL;unsigned long flags;if (size < SLOB_BREAK1)//根据要分配的size选择合适的链表slob_list = &free_slob_small;else if (size < SLOB_BREAK2)slob_list = &free_slob_medium;elseslob_list = &free_slob_large;spin_lock_irqsave(&slob_lock, flags);list_for_each_entry(sp, slob_list, lru) {//遍历slob_list,注意这里是通过list中的lru结构计算偏移进而找到page/* Enough room on this page? */if (sp->units < SLOB_UNITS(size))continue;/* Attempt to alloc */prev = sp->lru.prev;b = slob_page_alloc(sp, size, align);//页内分配slob_blockif (!b)continue;if (prev != slob_list->prev &&slob_list->next != prev->next)// 将slob_list 头移动到当前分配页之前list_move_tail(slob_list, prev->next);break;}spin_unlock_irqrestore(&slob_lock, flags);/* Not enough space: must allocate a new page */if (!b) {b = slob_new_pages(gfp & ~__GFP_ZERO, 0, node);//分配slob页if (!b)return NULL;sp = virt_to_page(b);__SetPageSlab(sp);spin_lock_irqsave(&slob_lock, flags);sp->units = SLOB_UNITS(PAGE_SIZE);sp->freelist = b;INIT_LIST_HEAD(&sp->lru);set_slob(b, SLOB_UNITS(PAGE_SIZE), b + SLOB_UNITS(PAGE_SIZE));set_slob_page_free(sp, slob_list);//将page中lru加入slob_list链表,注意插入到head的下一个;b = slob_page_alloc(sp, size, align);//页内分配slob_blockBUG_ON(!b);spin_unlock_irqrestore(&slob_lock, flags);}if (unlikely((gfp & __GFP_ZERO) && b))memset(b, 0, size);return b;
}
/** Allocate a slob block within a given slob_page sp.*/
static void *slob_page_alloc(struct page *sp, size_t size, int align)
{slob_t *prev, *cur, *aligned = NULL;int delta = 0, units = SLOB_UNITS(size);for (prev = NULL, cur = sp->freelist; ; prev = cur, cur = slob_next(cur)) {//通过偏移遍历页内slob_blockslobidx_t avail = slob_units(cur);if (align) {//如果有对齐要求,则将需要对齐的部分切割出来aligned = (slob_t *)ALIGN((unsigned long)cur, align);delta = aligned - cur;}if (avail >= units + delta) { /* room enough? */slob_t *next;if (delta) { /* need to fragment head to align? */next = slob_next(cur);set_slob(aligned, avail - delta, next);set_slob(cur, delta, aligned);prev = cur;cur = aligned;avail = slob_units(cur);}next = slob_next(cur);//对齐部分处理完成if (avail == units) { //当前block空间与要分配内容大小相等if (prev)set_slob(prev, slob_units(prev), next);//将前边block的偏移计算到next block的位置elsesp->freelist = next;//是页内第一块则直接将freelist指定到next} else { /* fragment */if (prev)set_slob(prev, slob_units(prev), cur + units);//将前边block的偏移计算到分配剩余的位置elsesp->freelist = cur + units;//页内第一个则链接到freelist上set_slob(cur + units, avail - units, next);//当前block计算到next的偏移,更新}sp->units -= units;//更新页内剩余 units的大小if (!sp->units)//满了就从slob_list中移除clear_slob_page_free(sp);return cur;}if (slob_last(cur))//存在一种情况,页内的block均比待分配size小,则返回NULL;return NULL;}
}
2.2 释放逻辑
释放时主要考虑位置的不同,分为多种情况:
code 注释:
/** slob_free: entry point into the slob allocator.*/
static void slob_free(void *block, int size)
{struct page *sp;slob_t *prev, *next, *b = (slob_t *)block;slobidx_t units;unsigned long flags;struct list_head *slob_list;if (unlikely(ZERO_OR_NULL_PTR(block)))return;BUG_ON(!size);sp = virt_to_page(block);units = SLOB_UNITS(size);spin_lock_irqsave(&slob_lock, flags);if (sp->units + units == SLOB_UNITS(PAGE_SIZE)) {//释放掉这个block,整页都free/* Go directly to page allocator. Do not pass slob allocator */if (slob_page_free(sp))clear_slob_page_free(sp);spin_unlock_irqrestore(&slob_lock, flags);__ClearPageSlab(sp);page_mapcount_reset(sp);slob_free_pages(b, 0);return;}if (!slob_page_free(sp)) {//原来是full的,需要加入slob_list中/* This slob page is about to become partially free. Easy! */sp->units = units;//记录free的unitssp->freelist = b;//标记block位置set_slob(b, units, (void *)((unsigned long)(b + SLOB_UNITS(PAGE_SIZE)) & PAGE_MASK));//加入合适的slob_listif (size < SLOB_BREAK1) slob_list = &free_slob_small;else if (size < SLOB_BREAK2) slob_list = &free_slob_medium;else slob_list = &free_slob_large;set_slob_page_free(sp, slob_list);goto out;}//原来就是部分空闲的pagesp->units += units;//标记剩余空间大小if (b < (slob_t *)sp->freelist) {//释放位置在最开始,则更新freelistif (b + units == sp->freelist) {units += slob_units(sp->freelist);sp->freelist = slob_next(sp->freelist);}set_slob(b, units, sp->freelist);sp->freelist = b;} else {//其他情况,找到对应prev = sp->freelist;next = slob_next(prev);while (b > next) {//先找到要释放的位置prev = next;next = slob_next(prev);}if (!slob_last(prev) && b + units == next) {//可以和next block连在一起不?units += slob_units(next);set_slob(b, units, slob_next(next));} else //标记当前block的位置和到下一个的偏移set_slob(b, units, next);if (prev + slob_units(prev) == b) {//可以和prev block连在一起不?units = slob_units(b) + slob_units(prev);set_slob(prev, units, slob_next(b));} else //标记prev到当前位置的偏移set_slob(prev, slob_units(prev), b);}
out:spin_unlock_irqrestore(&slob_lock, flags);
}
slob分配器对外提供两套接口:
- kmalloc 指定obj size直接从链表中分配空间;
- kmem_cache 则维护一个kmem_cache的对象,从其中分配固定大小的空间;
附录
涉及相关文件目录
目录 说明 /mm/slob.c slob分配器code实现部分 /include/linux/list.h 涉及到list操作的定义实现部分 /include/linux/kernel.h 涉及到相关宏的依赖 /include/linux/mm_types.h page结构体的定义 本文中code均基于kernel-4.9版本分析
Slob分配器的数据结构和分配逻辑相关推荐
- Linux Slob分配器(二)--分配对象
each 水平有限,描述不当之处还请指出,转载请注明出处http://blog.csdn.net/vanbreaker/article/details/7705559 上节介绍了Slob分配器的相关概 ...
- LINUX内核狂想曲之SLOB分配器
LINUX内核狂想曲 @CopyLeft by ICANTH,I Can do ANy THing that I CAN THink!~ Author: WenHui, WuHan Universit ...
- Linux Slob分配器(一)--概述
水平有限,描述不当之处还请指出,转载请注明出处 http://blog.csdn.net/vanbreaker/article/details/7705202 Slob分配器相较Slab和Slub分配 ...
- 贷后风控中逾期案件差异化的分配逻辑
在风控精细化运营的当下,贷后工作的开展,越来越需要精细化管理. 今天我们总结梳理了番茄风控之前的内容并结合近期开展的内容,为大家带来一个催收模型优化和逾期案件催收差异化策略的内容,详细请看本文介绍. ...
- 贷后联动管控指标与差异化案件的分配逻辑
在风控精细化运营的当下,贷后工作的开展,越来越需要精细化管理.如何做好相关的精细化管理工作,首先我们从这些贷后相关的名词如下开始熟悉: 贷后基本催收名词解释 Flow Rate 迁移率就是在贷后资产评 ...
- 4K HDMI分配器 1路HDMI分配2路同步信号输出
朗强LKV312RPO HDMI1.4分配器是朗强最新研发并生产的数字高清分配器,可以将1路的HDMI信号分配成2路同步的HDMI信号输出,提升支持4K*2K的分辨率的分配与支持,对输出的同步信号采用 ...
- 数据结构-算法: 分配排序(基数分配排序法)
基数排序(Radix Sort)是对箱排序的改进和推广. 1.单关键字和多关键字 文件中任一记录R[i]的关键字均由d个分量 构成. 若这d个分量中每 ...
- SAP Spartacus ConsentTemplate 数据结构的暴露逻辑
我基于SAP Spartacus 2.1创建的sample store,里面导入了ConsentTemplate这个数据结构. import { ActiveCartService, CartAddE ...
- 数据结构-排序-分配类排序-知识点总结归纳3
分配类排序:核心是分配和收集,利用关键字的优先级进行排序的思想 高位优先排序:比如桥牌,先比较花色在比较面值;比如学号,比较级,院,班,号; 低位优先排序: 链式基数排序 思想:基于"分配& ...
- 数据结构4种逻辑关系,get到你就是强者!!!!
逻辑结构 数据元素间抽象化的相互关系,与数据的存储无关,独立于计算机,它是从具体问题抽象出来的数学模型. 4种逻辑关系 1.集合--数据元素间除"同属于一个集合"外,无其它关系 2 ...
最新文章
- 互联网协议 — NTP 时间同步协议
- Ubuntu 安装 opencv-nonfree
- HDU-1274 展开字符串
- GMIS 2017大会漆远演讲:AI 驱动金融生活
- python matplotlab.pyplot.scatter() 函数的用法
- Jupyter notebook入门教程(下)
- 微软跨平台移动开发工具套件HockeyApp宣布免费
- Android 自定义阴影,自定义颜色样式
- hdu 2035 人见人爱A^B (快速幂)
- python求解给定一个整数N,求N!末尾有多少个0,求N!的二进制中最低位1的位置
- matlab通过带通滤波器代码,设计一个matlab带通滤波器代码
- 【工作周报】2019年7月 前端开发工作周报汇总
- 基于Matlab/Simulink的1/4车辆动力学模型
- oracle如何做定时任务,oracle实现定时任务
- IntelliJ IDEA 在 Project 选项卡中查找快捷键
- linux ls和ll命令学习小结
- 网易云易盾朱星星:最容易被驳回的10大APP过检项
- C#方法,可空类型,数组,集合,ArrayList排序,List,Hashtable和Dictionary
- jquery getJSON不执行问题解决
- Android笔记(十九)制作一个简易的指南针
热门文章
- 本机是wifi,虚拟机无法连接外网问题
- 二 Djano模型层之模型字段选项
- Could not create ServerSocket on address 0.0.0.0/0.0.0.0:9083
- TDirectory.IsRelativePath是否相对路径
- 【面试】求数组子序列的最大和
- xgboost算法_xgboost算法学习心得
- 跟我一起学docker(14)--docker swarm的使用
- Java经典实例:在正则表达式中控制大小写
- Linux终端的总结和shell
- C语言printf语法