背景

  • Read the fucking source code! --By 鲁迅
  • A picture is worth a thousand words. --By 高尔基

说明:

  1. Kernel版本:4.14
  2. ARM64处理器,Contex-A53,双核
  3. 使用工具:Source Insight 3.5, Visio

1. 概述

之前的文章分析的都是基于页面的内存分配,而小块内存的分配和管理是通过块分配器来实现的。目前内核中,有三种方式来实现小块内存分配:slab, slub, slob,最先有slab分配器,slub/slob分配器是改进版,slob分配器适用于小内存嵌入式设备,而slub分配器目前已逐渐成为主流块分配器。接下来的文章,就是以slub分配器为目标,进一步深入。

先来一个初印象:

2. 数据结构

有四个关键的数据结构:

  • struct kmem_cache:用于管理SLAB缓存,包括该缓存中对象的信息描述,per-CPU/Node管理slab页面等;

    关键字段如下:
/*
* Slab cache management.
*/
struct kmem_cache {
struct kmem_cache_cpu __percpu *cpu_slab; //每个CPU slab页面
/* Used for retriving partial slabs etc */
unsigned long flags;
unsigned long min_partial;
int size; /* The size of an object including meta data */
int object_size; /* The size of an object without meta data */
int offset; /* Free pointer offset. */
#ifdef CONFIG_SLUB_CPU_PARTIAL
/* Number of per cpu partial objects to keep around */
unsigned int cpu_partial;
#endif
struct kmem_cache_order_objects oo; //该结构体会描述申请页面的order值,以及object的个数 /* Allocation and freeing of slabs */
struct kmem_cache_order_objects max;
struct kmem_cache_order_objects min;
gfp_t allocflags; /* gfp flags to use on each alloc */
int refcount; /* Refcount for slab cache destroy */
void (*ctor)(void *); // 对象构造函数
int inuse; /* Offset to metadata */
int align; /* Alignment */
int reserved; /* Reserved bytes at the end of slabs */
int red_left_pad; /* Left redzone padding size */
const char *name; /* Name (only for display!) */
struct list_head list; /* List of slab caches */ //kmem_cache最终会链接在一个全局链表中
struct kmem_cache_node *node[MAX_NUMNODES]; //Node管理slab页面
};
  • struct kmem_cache_cpu:用于管理每个CPU的slab页面,可以使用无锁访问,提高缓存对象分配速度;
struct kmem_cache_cpu {
void **freelist; /* Pointer to next available object */ //指向空闲对象的指针
unsigned long tid; /* Globally unique transaction id */
struct page *page; /* The slab from which we are allocating */ //slab缓存页面
#ifdef CONFIG_SLUB_CPU_PARTIAL
struct page *partial; /* Partially allocated frozen slabs */
#endif
#ifdef CONFIG_SLUB_STATS
unsigned stat[NR_SLUB_STAT_ITEMS];
#endif
};
  • struct kmem_cache_node:用于管理每个Node的slab页面,由于每个Node的访问速度不一致,slab页面由Node来管理;
/*
* The slab lists for all objects.
*/
struct kmem_cache_node {
spinlock_t list_lock; #ifdef CONFIG_SLUB
unsigned long nr_partial; //slab页表数量
struct list_head partial; //slab页面链表
#ifdef CONFIG_SLUB_DEBUG
atomic_long_t nr_slabs;
atomic_long_t total_objects;
struct list_head full;
#endif
#endif
};
  • struct page:用于描述slab页面struct page结构体中很多字段都是通过union联合体进行复用的。

    struct page结构中,用于slub的成员如下:
struct page {
union {
...
void *s_mem; /* slab first object */
...
}; /* Second double word */
union {
...
void *freelist; /* sl[aou]b first free object */
...
}; union {
...
struct {
union {
...
struct { /* SLUB */
unsigned inuse:16;
unsigned objects:15;
unsigned frozen:1;
};
...
};
...
};
}; /*
* Third double word block
*/
union {
...
struct { /* slub per cpu partial pages */
struct page *next; /* Next partial slab */
#ifdef CONFIG_64BIT
int pages; /* Nr of partial slabs left */
int pobjects; /* Approximate # of objects */
#else
short int pages;
short int pobjects;
#endif
}; struct rcu_head rcu_head; /* Used by SLAB
* when destroying via RCU
*/
};
...
struct kmem_cache *slab_cache; /* SL[AU]B: Pointer to slab */
...
}

图来了:

3. 流程分析

针对Slub的使用,可以从三个维度来分析:

  1. slub缓存创建
  2. slub对象分配
  3. slub对象释放

下边将进一步分析。

3.1 kmem_cache_create

在内核中通过kmem_cache_create接口来创建一个slab缓存

先看一下这个接口的函数调用关系图:

  1. kmem_cache_create完成的功能比较简单,就是创建一个用于管理slab缓存kmem_cache结构,并对该结构体进行初始化,最终添加到全局链表中。kmem_cache结构体初始化,包括了上文中分析到的kmem_cache_cpukmem_cache_node两个字段结构。

  2. 在创建的过程中,当发现已有的slab缓存中,有存在对象大小相近,且具有兼容标志的slab缓存,那就只需要进行merge操作并返回,而无需进一步创建新的slab缓存

  3. calculate_sizes函数会根据指定的force_order或根据对象大小去计算kmem_cache结构体中的size/min/oo等值,其中kmem_cache_order_objects结构体,是由页面分配order值和对象数量两者通过位域拼接起来的。

  4. 在创建slab缓存的时候,有一个先鸡后蛋的问题:kmem_cache结构体来管理一个slab缓存,而创建kmem_cache结构体又是从slab缓存中分配出来的对象,那么这个问题是怎么解决的呢?可以看一下kmem_cache_init函数,内核中定义了两个静态的全局变量kmem_cachekmem_cache_node,在kmem_cache_init函数中完成了这两个结构体的初始化之后,相当于就是创建了两个slab缓存,一个用于分配kmem_cache结构体对象的缓存池,一个用于分配kmem_cache_node结构体对象的缓存池。由于kmem_cache_cpu结构体是通过__alloc_percpu来分配的,因此不需要创建一个相关的slab缓存

3.2 kmem_cache_alloc

kmem_cache_alloc接口用于从slab缓存池中分配对象。

看一下大体的调用流程图:

从上图中可以看出,分配slab对象与Buddy System中分配页面类似,存在快速路径和慢速路径两种,所谓的快速路径就是per-CPU缓存,可以无锁访问,因而效率更高。

整体的分配流程大体是这样的:优先从per-CPU缓存中进行分配,如果per-CPU缓存中已经全部分配完毕,则从Node管理的slab页面中迁移slab页per-CPU缓存中,再重新分配。当Node管理的slab页面也不足的情况下,则从Buddy System中分配新的页面,添加到per-CPU缓存中。

还是用图来说明更清晰,分为以下几步来分配:

  1. fastpath

    快速路径下,以原子的方式检索per-CPU缓存的freelist列表中的第一个对象,如果freelist为空并且没有要检索的对象,则跳入慢速路径操作,最后再返回到快速路径中重试操作。

  2. slowpath-1

    将per-CPU缓存中page指向的slab页中的空闲对象迁移到freelist中,如果有空闲对象,则freeze该页面,没有空闲对象则跳转到slowpath-2

  3. slowpath-2

    将per-CPU缓存中partial链表中的第一个slab页迁移到page指针中,如果partial链表为空,则跳转到slowpath-3

  4. slowpath-3

    将Node管理的partial链表中的slab页迁移到per-CPU缓存中的page中,并重复第二个slab页将其添加到per-CPU缓存中的partial链表中。如果迁移的slab中空闲对象超过了kmem_cache.cpu_partial的一半,则仅迁移slab页,并且不再重复。

    如果每个Node的partial链表都为空,跳转到slowpath-4

  5. slowpath-4

    Buddy System中获取页面,并将其添加到per-CPU的page中。

3.2 kmem_cache_free

kmem_cache_free的操作,可以看成是kmem_cache_alloc的逆过程,因此也分为快速路径和慢速路径两种方式,同时,慢速路径中又分为了好几种情况,可以参考kmem_cache_alloc的过程。

调用流程图如下:

效果如下:

  1. 快速路径释放

    快速路径下,直接将对象返回到freelist中即可。

  2. put_cpu_partial

    put_cpu_partial函数主要是将一个刚freeze的slab页,放入到partial链表中。

    put_cpu_partial函数中调用unfreeze_partials函数,这时候会将per-CPU管理的partial链表中的slab页面添加到Node管理的partial链表的尾部。如果超出了Node的partial链表,溢出的slab页面中没有分配对象的slab页面将会返回到伙伴系统。

  3. add_partial

    添加slab页到Node的partial链表中。

  4. remove_partial

    从Node的partial链表移除slab页。

具体释放的流程走哪个分支,跟对象的使用情况,partial链表的个数nr_partial/min_partial等相关,细节就不再深入分析了。

【原创】(十一)Linux内存管理slub分配器的更多相关文章

  1. Linux内存管理 - slab分配器和kmalloc

    本文目的在于分析Linux内存管理机制的slab分配器.内核版本为2.6.31.1. SLAB分配器 内核需要经常分配内存,我们在内核中最常用的分配内存的方式就是kmalloc了.前面讲过的伙伴系统只 ...

  2. Linux内存管理 (5)slab分配器

    专题:Linux内存管理专题 关键词:slab/slub/slob.slab描述符.kmalloc.本地/共享对象缓冲池.slabs_partial/slabs_full/slabs_free.ava ...

  3. Linux内存管理之slab分配器

    slab分配器是什么? 参考:http://blog.csdn.net/vanbreaker/article/details/7664296 slab分配器是Linux内存管理中非常重要和复杂的一部分 ...

  4. 【原创】(六)Linux内存管理 - zoned page frame allocator - 1

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...

  5. Linux内存管理原理

    本文以32位机器为准,串讲一些内存管理的知识点. 1. 虚拟地址.物理地址.逻辑地址.线性地址 虚拟地址又叫线性地址.linux没有采用分段机制,所以逻辑地址和虚拟地址(线性地址)(在用户态,内核态逻 ...

  6. Linux内存管理原理【转】

    转自:http://www.cnblogs.com/zhaoyl/p/3695517.html 本文以32位机器为准,串讲一些内存管理的知识点. 1. 虚拟地址.物理地址.逻辑地址.线性地址 虚拟地址 ...

  7. Windows内存管理和linux内存管理

    windows内存管理 windows 内存管理方式主要分为:页式管理,段式管理,段页式管理. 页式管理的基本原理是将各进程的虚拟空间划分为若干个长度相等的页:页式管理把内存空间按照页的大小划分成片或 ...

  8. Linux内存管理专题

    Linux的内存管理涉及到的内容非常庞杂,而且与内核的方方面面耦合在一起,想要理解透彻非常困难. 在开始学习之前进行了一些准备工作<如何展开Linux Memory Management学习?& ...

  9. 伙伴系统之伙伴系统概述--Linux内存管理(十五)

    在内核初始化完成之后, 内存管理的责任就由伙伴系统来承担. 伙伴系统基于一种相对简单然而令人吃惊的强大算法. Linux内核使用二进制伙伴算法来管理和分配物理内存页面, 该算法由Knowlton设计, ...

随机推荐

  1. PHP代码审计基础-高级篇

    高级篇主要讲 1. 熟知各个开源框架历史版本漏洞. 2. 业务逻辑漏洞 3. 多线程引发的漏洞 4. 事务锁引发的漏洞 在高级篇审计中有很多漏洞正常情况下是不存在的只有在特殊情况下才有 PHP常用框架 ...

  2. 程序员修神之路--为什么有了SOA,我们还用微服务?

    菜菜哥,我最近需要做一个项目,老大让我用微服务的方式来做 那挺好呀,微服务现在的确很流行 我以前在别的公司都是以SOA的方式,SOA也是面向服务的方式呀 的确,微服务和SOA有相同之处 面向服务的架构 ...

  3. 2019.10.28 CSP%您赛第四场t3

    我写不动前两个了. 原谅一下. ____________________________________________________________________________________ ...

  4. Distinct 实现自定义去重

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  5. FreeSql 已支持 .NetFramework 4.0、ODBC 访问

    FreeSql 开源发布快一年了,目前主仓库代码量 64118 行,用 git 命令统计的命令如下: find . "(" -name "*.cs" " ...

  6. 设计模式(十三)Visitor模式

    Visitor模式可以用来把数据结构与处理分离开.通俗来说就是编写一个访问者类来访问数据结构中的元素,并把对各元素的处理交给访问者类.这样,当需要增加新的处理时,只需要编写新的访问者,然后让数据结构可 ...

  7. SpringBoot整合FastJson(七)

    一.Maven依赖 <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson&l ...

  8. AssetBundle异步加载被中断的问题

    刘 刘泰言创建于 1 年前 在使用异步接口 yield return AssetBundle.ASyncLoad的时候,难免会想到:这个异步处理完之前如何Cancel掉这个任务?也就是一个AssetB ...

  9. Flask:Flask的模板系统和静态文件

    1.Flask模板系统 Django框架有自己独立的模板系统,而Flask是没有的,Flask默认采用jinjia2模板系统,jinjia2是仿写Django模板系统的一个第三方模块,但性能上要比Dj ...

  10. 练习Markdown基本语法

    这是一级标题 二级标题 三级标题 我就说一点(数字+英文句点.) 第二 在行首增加*或-,就会有下面的效果 嘿 嘿 嘿 这一部分是插入图片和引用 插入图片 用感叹号+[]+括号 直接复制粘贴~ 引用 ...