Slab作为一种内存管理方案,其作用主要有以下2点:

a) 避免频繁的内存分配释放造成的内存碎片

b) 减少内存分配操作产生的性能开销

Linux内核数据结构中也有slab的设计,Linux提供了一套接口,使用这套接口可以动态创建与释放一个slab结构,该slab的chunk大小通过接口指定, 创建成功后就可以从该slab中动态申请与释放chunk大小的内存用于存储目标数据,例如内核中用于表示进程的结构体task_struc就是使用的slab方式进行管理。memcached与linux内核不同,memcached中在程序启动时即初始化一个slabclass_t的全局数组,每一个slabclass_t结构的chunk大小不同,构成一个全局slab池。memcached中的slab相关操作源码主要集中在源文件slabs.c中,下面对它进行分析。

1.    结构体slabclass_t

以下是memcached中对slabclass_t的定义,

typedef struct {
unsigned int size; /* sizes of items */
unsigned int perslab; /* how many items per slab */ void *slots; /* list of item ptrs */
unsigned int sl_curr; /* total free items in list */ unsigned int slabs; /* how many slabs were allocated for this class */ void **slab_list; /* array of slab pointers */
unsigned int list_size; /* size of prev array */ size_t requested; /* The number of requested bytes */
} slabclass_t;

slabclass_t对内存的组织可以粗略的用图1-1表示,

图1-1 slabclass_t内存组织方式

slab_list是一个可以动态分配的数组,数组大小以list_size表示,数组中已存储元素数目以slabs表示,数组中每一个元素都表示一个page大小的可用内存(page也称为slab)。在为该slabclass_t新增加内存时,如果slabs等于list_size即表示数据空间已全部占用,需要使用realloc重新分配数组。

slabclass_t中的可用内存总是切割为size大小的chunks,一个page共可以切分为perslab个chunk。

slabclass_t中未被使用的chunk以单链表的方式进行组织,slots即是该单链表的链表头。当从该slabclass_t中申请内存时,返回slots指向的第一个chunk内存;当使用者释放从slabclass_t中申请的内存时,总是插入该chunk到链表头。sl_curr表示当前空闲的chunk数目,即单链表的元素数目。

为了构成单链表,每一个chunk中总是需要存在一个next指针,事实上,每一个chunk大小至少是一个item结构体的大小, memcached中以item表示一个存储对象,item的数据及item结构体实例总是被存储在同一个chunk中,数据紧接在结构体实例之后。而item中包含一个next指针,slabclass_t复用了该指针来组织空闲的chunk。

当为该slabclass_t新增加一个page的可用内存时,该page会被切分为一个一个的空闲chunk,并被插入到slots指向的单链表头。因此slabclass_t中的内存要么属于空闲chunk,由slots管理,要么属于被使用的item,被hashtable索引。

2.    特殊的slabclass[0]

memcached中以一个全局数组slabclass[MAX_NUMBER_OF_SLAB_CLASSES]存储所有的slabclass_t,每一个元素具有不同的chunk大小。但是这之中,slabclass[0]被设计成一个特殊的slabclass_t,item不会存储在slabclass[0]之中,相反,slabclass[0]用来为其它slabclass_t提供内存。

由于item不会存储在slabclass[0]之中,因此slabclass[0]中的slab_list中指向的page都是可用的,当其它slabclass_t需要增加一个page时,可以直接将slab_list中的最后一个page直接移到目的slabclass_t的slab_list的末尾。如果slabclass[0]不存在可用page时,再使用malloc或者其它方式分配内存。

3.    slabclass_t之间移动page

除了从特殊的slabclass[0]移出page以外,有时也需要在其它slabclass_t之间移动page,以根据需要平衡内存分布。Memcached根据一定的策略选择源与目的slabclass_t,将源slabclass_t的slab_list[0]移动到目的slabcalss_t的slab_list末尾。由于可能有item存储在源端的slab_list[0]指向的内存之中,因此在移动page之前需要做一定的处理。移动page的处理流程可以大致以图3-1表示。

图3-1 移动page的处理流程

4.    部分相关函数功能说明

void slabs_init(const size_t limit, const double factor, const bool prealloc, const uint32_t *slab_sizes) 

初始化全局slabclass[MAX_NUMBER_OF_SLAB_CLASSES],确定每一级slabclass_t的chunk 大小。limit代表slabs可以分配的内存上限;factor表示每一级slabclass_t的chunk大小的倍增因子;prealloc代表是否预分配一块内存;slab_sizes是一个整形数组,如果提供了该数组,各级slabclass_t的chunk大小由数组中的值决定。

static int grow_slab_list (const unsigned int id)

确保slabclass[id]中的slab_list数组有足够的空间容纳新增加page,如果空间不足则调用realloc重新分配空间。

static void split_slab_page_into_freelist(char *ptr, const unsigned int id) 

将新分配的page切割成chunk,并将chunk连接到slabclass[id]的slots链表头。

static int do_slabs_newslab(const unsigned int id) 

为slabclass[id]增加一个新的page,它会调用grow_slab_list确保slab_list空间,从slabclass[0]或者其它方式获得新的page,调用split_slab_page_into_freelist处理新的page。

static void *do_slabs_alloc(const size_t size, unsigned int id, uint64_t *total_bytes, unsigned int flags) 

从slabclass[id]中获取一个未使用的chunk,除了做一些必要的检查之外,获取的方式相当简单,直接从slots链表头获取一个chunk即可。另外返回的chunk会做一些初始化设置,如清除ITEM_SLABBED标志,表示该chunk已被使用,设置引用计数为1等。

static void do_slabs_free(void *ptr, const size_t size, unsigned int id)

释放ptr指向的item,新该chunk重新插入到slots链表头。初始化一些参数,如flag设置为ITEM_SLABBED表示该chunk未被使用。参数size为item实际使用的内存大小,用作一些信息统计,并非chunk的大小。另外有一种称为chunked的item(见其它解析),它的flag被设置为ITEM_CHUNKED,释放时需要调用do_slabs_free_chunked函数,以释放相关的chunk。

static int slab_rebalance_move(void)

做移动page时的处理操作,确保要移动的page中存储的item已被移动到其它的page中。

memcached中有一个称为slab_rebalance_thread的线程,该线程的作为即在各个slabclass_t中移动page,平衡内存分配。线程中使用一个条件信号量slab_rebalance_cond,当没有平衡任务时,线程一直等待该信号量,处于休眠状态。外界调用slabs_reassign函数启动平衡任务,该函数将发送条件信号量,激活线程。线程按照图3-1的流程,调用slab_rebalance_start设置内存检查pos等,然后调用slab_rebalance_move完成page清理工作,最后调用函数slab_rebalance_finish完成平衡任务,线程再次进入休眠,等待新的平衡任务。

memcached源码分析一-slab的更多相关文章

  1. Memcached源码分析之内存管理

    先再说明一下,我本次分析的memcached版本是1.4.20,有些旧的版本关于内存管理的机制和数据结构与1.4.20有一定的差异(本文中会提到). 一)模型分析在开始解剖memcached关于内存管 ...

  2. memcached源码分析-----item过期失效处理以及LRU爬虫

    memcached源码分析-----item过期失效处理以及LRU爬虫,memcached-----item 转载请注明出处:http://blog.csdn.net/luotuo44/article ...

  3. Memcached源码分析之请求处理(状态机)

    作者:Calix 一)上文 在上一篇线程模型的分析中,我们知道,worker线程和主线程都调用了同一个函数,conn_new进行事件监听,并返回conn结构体对象.最终有事件到达时,调用同一个函数ev ...

  4. Memcached源码分析之线程模型

    作者:Calix 一)模型分析 memcached到底是如何处理我们的网络连接的? memcached通过epoll(使用libevent,下面具体再讲)实现异步的服务器,但仍然使用多线程,主要有两种 ...

  5. Memcached源码分析之从SET命令开始说起

    作者:Calix 如果直接把memcached的源码从main函数开始说,恐怕会有点头大,所以这里以一句经典的“SET”命令简单地开个头,算是回忆一下memcached的作用,后面的结构篇中关于命令解 ...

  6. Memcached源码分析

    作者:Calix,转载请注明出处:http://calixwu.com 最近研究了一下memcached的源码,在这里系统总结了一下笔记和理解,写了几 篇源码分析和大家分享,整个系列分为“结构篇”和“ ...

  7. memcached源码分析-----slab内存分配器

    温馨提示:本文用到了一些可以在启动memcached设置的全局变量.关于这些全局变量的含义可以参考<memcached启动参数详解>.对于这些全局变量,处理方式就像<如何阅读memc ...

  8. Memcached源码分析——process_command函数解析

    以下为个人笔记 /** * process_command 在memcached中是用来处理用户发送的命令的, * 包括get set,add,delete,replace,stats,flush_a ...

  9. Memcached源码分析——内存管理

    注:这篇内容极其混乱 推荐学习这篇博客.博客的地址:http://kenby.iteye.com/blog/1423989 基本元素item item是Memcached中记录存储的基本单元,用户向m ...

随机推荐

  1. html5权威指南:组织内容、文档分节

    HTML5新增及删除标签:http://www.cnblogs.com/starof/archive/2015/06/23/4581850.html 第九章:组织内容                 ...

  2. vue+element打印页面功能

    项目中遇到了要打印页面的功能,我感之前使用的是一个第三方的插件,但是不兼容IE,后来发现直接调用window的API就可以了,MMP // 打印表单 printBtn() { window.print ...

  3. Fundebug 微信小程 BUG 监控插件更新至 1.2.1,优化错误上报次数的限制算法,新增 silentHttpHeader 配置选项

    摘要: 1.2.1优化错误上报次数的限制算法,新增silentHttpHeader配置选项,请大家及时更新哈! Fundebug提供专业的微信小程序 BUG 监控服务,可以第一时间为您捕获生存环境中小 ...

  4. MySQL5.7 安装和配置环境变量

    安装 1.下载安装包 官网地址:https://dev.mysql.com/downloads/mysql/ 2.选择 Custom,自定义 3.根据自己系统选择 x64还是x86,然后点击第一个箭头 ...

  5. Eclipse调用Tomcat出错

    错误提示:The server cannot be started because one or more of the ports are invalid. Open the server edit ...

  6. JDBC连接池的九种查询

    package JDBC_Demo; import java.sql.SQLException; import java.util.List; import java.util.Map; import ...

  7. 进程及Python实现

    进程杂谈 #进程就是正在执行的一个过程,是对正在运行程序的一个抽象 #进程由程序.数据集和进程控制块(最重要的,进程切换 状态如何保存,恢复和记录)组成 """ 进程调度 ...

  8. Win10解决修改host没有权限问题(其他文件同理) 一步都不能少哦:先添加再授权

    Step1:右键文件选择属性,选择安全,点击编辑: Step2:在弹窗中点击添加,在弹窗中点击高级: Step3:在弹窗中点击立即查找,选中当前用户,点击确定: Step4:此时选中用户已经被加入进来 ...

  9. Box HDU - 2475 (Splay 维护森林)

    Box \[ Time Limit: 5000 ms \quad Memory Limit: 32768 kB \] 题意 给出 \(n\) 个箱子的包含关系,每次两种操作. 操作 \(1\):把 \ ...

  10. LeetCode 1150. Check If a Number Is Majority Element in a Sorted Array

    原题链接在这里:https://leetcode.com/problems/check-if-a-number-is-majority-element-in-a-sorted-array/ 题目: G ...