slab翻译对slab内存管理源代码的说明

更新时间:2023-07-19 22:25:01 阅读量: 实用文档 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

slab翻译对slab内存管理源代码的说明

slab翻译:对slab内存管理源代码的说明

疯狂代码 / :http://VC/Article13740.html

学习计算机原理最好是实战或看高手写源代码在定程度上就不再会感到原理抽象有关slab些原理资料可以在这里下载或到网站WebSite有更多信息和资料Slab内存管理机制已被广泛使用要找到使用slab管理内存开源代码也不难如些OS内核中内存管理既然要分析理解slab最好还是选择复杂度和代码量都不要太大在这里我选取了glib-2.12.9gslice.c实现slab机制相关代码作为分析对象注意Glib库是针对用户级而非OS内核级别 gslice.c中实现了 3种内存分配机制:是slab; 2是比slab更适合于多CPU/多线程magazine; 3是只使用纯粹malloc本文章只针对slab相关源代码进行分析

在分析代码时主要从以下几个方面入手:先从分配器总体数据结构关系进行描述; 2是看分配器allocator是如何化;接下来是分析分配器如何分配和回收内存(chunk)

Allocator分配器总体结构:

下面先来看些重要数据结构和变量:

…………. //点代表省略代码

130 typedef struct _ChunkLink ChunkLink;

131 typedef struct _SlabInfo SlabInfo;

132 typedef struct _CachedMagazine CachedMagazine;

// 这个结构也表明了个Chunk最小值是两个指针大小

133 struct _ChunkLink {

134 ChunkLink *next;

135 ChunkLink *data; //这字段在slab中未被使用

136 };

137 struct _SlabInfo {

slab翻译对slab内存管理源代码的说明

138 ChunkLink *chunks;

139 gu n_allocated;

140 SlabInfo *next, *prev;

141 };

………….

150 typedef struct {

151 gboolean always_malloc; // 为TRUE表示使用纯粹malloc

152 gboolean bypass_magazines; // 为TRUE表示使用slab

153 gsize working__msecs;

154 gu color_increment;

155 } SliceConfig;

156 typedef struct {

157 /* const after initialization */

158 gsize min_page_size, max_page_size;

159 SliceConfig config;

……………

slab翻译对slab内存管理源代码的说明

168 /* slab allocator */

169 GMutex *slab_mutex;

// SlabInfo指针最大值为MAX_SLAB_INDEX (allocator)

170 SlabInfo **slab_stack; /* .gif' /> of MAX_SLAB_INDEX (allocator) */

171 gu color_accu;

172 } Allocator;

………….

// 这个变量如果是0介绍说明allocator还未被化如果是大于0数介绍说明allocator

// 已被化并且它值就是系统页面值大小

189 gsize sys_page_size = 0;

190 Allocator allocator[1] = { { 0, }, }; // 内存分配器

// 在变量slice_config中配置选取用那种分配机制由以下值可知默认情况

// 是使用magazine分配机制

191 SliceConfig slice_config = {

192 FALSE, /* always_malloc */

193 FALSE, /* bypass_magazines */ // 把这个值设为TRUE,才真正使用slab

194 15 * 1000, /* working__msecs */

slab翻译对slab内存管理源代码的说明

195 1, /* color increment, alt: 0x7fffffff */

196 };

………….

根据以上数据结构和逻辑实现可以把它们关系用如下图表示:[Page]

\">

Allocator有个SlabInfo指针slab_stack成员stab_stack每个成员或者是空指针或是个指向SlabInfo双向循环链表双向循环链表中每个成员有个指针指向chunk链表表头而chunk链表中每个成员就是接口g_slice_alloc时被分配空间

当接口g_slice_alloc申请空间时根据申请空间大小通过宏SLAB_INDEX找到对应指针slab_stack正确下标找到对应slab_stack下标就要以找到相应SlabInfo双向循环链表也就可以找到Chunk链表并从Chunk链表取出个节点作为被申请空间返回即实际分配内存要先找到SlabInfo双向循环链表然后再通过它分配内存

要注意在下面分析中会经常用到上图中几个名词这些名词有SlabInfo指针(allocator->slab_stack)、SlabInfo双向循环链表、每个SlabInfo管理Chunk链表还有在下面分析时会把allocator->slab_stack[ix]叫当前SlabInfo

allocator分配器化:

化关系是:g_slice_alloc--->allocator_categorize--->g_slice_init_nomessage---> slice_config_init

// 以下相关代码是化190行定义allocator变量

751 g_slice_alloc (gsize mem_size)

752 {

slab翻译对slab内存管理源代码的说明

……………// 点代表省略代码

755 gu acat;

……………

757 acat = allocator_categorize (chunk_size);

……………

779 }

// 这个作用是获取allocator分配器分配机制

// 返回值:0表示使用纯粹malloc;1表示使用magazine;2表示使用slab

335 inline gu

336 allocator_categorize (gsize aligned_chunk_size)

337 {

……………

346 (!sys_page_size)

347 g_slice_init_nomessage ;

……………

357 }

slab翻译对slab内存管理源代码的说明

281 g_slice_init_nomessage (void)

282 {

……………

// 获取系统页面大小并把值赋给sys_page_size变量

287 #def G_OS_WIN32

288 {

289 SYSTEM_INFO system_info;

290 GetInfo (&system_info);

291 sys_page_size = system_info.dwPageSize;

292 }

293 #

294 sys_page_size = sysconf (_SC_PAGESIZE); /* = sysconf (_SC_PAGE_SIZE); = getpagesize; */

295 #end

……………

298 slice_config_init (&allocator->config);

299 allocator->min_page_size = sys_page_size;

……………

slab翻译对slab内存管理源代码的说明

// 建立SlabInfo指针(allocator->slab_stack)

// 里每个指针值都化成NULL值

323 allocator->slab_stack = g_0 (SlabInfo*, MAX_SLAB_INDEX (allocator));

……………

<!--[ !supportLists]-->333 }<!--[end]-->

264 slice_config_init (SliceConfig *config)

265 {

……………

// 通过使用191行代码(代码在总揽中已给出)定义slice_config化

// allocator中config以此决定了使用哪种分配机制

273 *config = slice_config;

……………

278 }

[Page]从以上代码可知如果只看化相关代码这过程极其简单!它主要做了 3样事情:是获得系统页面大小sys_page_size; 2是化config以此决定了allocator分配器使用分配机制; 3是建立了SlabInfo指针

slab翻译对slab内存管理源代码的说明

allocator分配器分配内存chunk:

在分析主要代码的前有必要先了解操作chunk_size字节对齐和求SlabInfo指针(

allocator->slab_stack)下标几个宏定义

chunk_size字节对齐是通过宏P2ALIGN来实现P2ALIGN是以P2ALIGNMENT字节对齐

// gsize和下文GLIB_SIZEOF_SIZE_T是同等意义它等于个指针字节数可见

// P2ALIGNMENT为两个指针字节数也即在总揽给出代码133行中声明Chunk最

//小字节数我们般假设在32位机器中个指针字节数为4

// 那么P2ALIGNMENT值为8

// 在下面分析中如果有假设数据字节数就认为P2ALIGNMENT值为8

103 # P2ALIGNMENT (2 * (gsize)) /* fits 2 poers (assumed to be 2 * GLIB_SIZEOF_SIZE_T below) */

// ALIGN功能是求size以base字节对齐数据这是种常用思路方法如果看不明白

// 可以假设些真实数据进去运算当然假设数据时base值最好是2n次方

104 # ALIGN(size, base) ((base) * (gsize) (((size) + (base) - 1) / (base)))

// 下面P2ALIGN也是以定字节数对齐操作它用了些 2进制窍门技巧

// 可以参考本人写另外篇文章讲 2进制窍门技巧那部分或有关这方面知识其它资料

116 /* optimized version of ALIGN (size, P2ALIGNMENT) */

117 # GLIB_SIZEOF_SIZE_T * 2 8 /* P2ALIGNMENT */

slab翻译对slab内存管理源代码的说明

118 # P2ALIGN(size) (((size) + 0x7) & ~(gsize) 0x7) // 以8字节对齐

119 #el GLIB_SIZEOF_SIZE_T * 2 16 /* P2ALIGNMENT */

120 # P2ALIGN(size) (((size) + 0xf) & ~(gsize) 0xf) // 以16字节对齐

121 #

122 # P2ALIGN(size) ALIGN (size, P2ALIGNMENT)

123 #end

求SlabInfo指针(allocator->slab_stack)下标宏是代码112行SLAB_INDEX(al, asize)从代码981行可知宏SLAB_INDEX里asize就是chunk_size代码981行chunk_size是从代码756行P2ALIGN获得可见传给宏

SLAB_INDEXasize已是P2ALIGNMENT字节对齐了下图更直接明了地介绍说明了chunk_size和SlabInfo指针(allocator->slab_stack)下标关系可见SlabInfo指针(allocator->slab_stack)下标从小到大成员所指向SlabInfo双向循环链表(此链表见总揽图)chunk_size是从小到大P2ALIGNMENT整数倍数

\">

// 通过asize求SlabInfo指针(allocator->slab_stack)下标

// asize以P2ALIGNMENT字节对齐

112 # SLAB_INDEX(al, asize) ((asize) / P2ALIGNMENT - 1) /* asize must be P2ALIGNMENT aligned */

slab翻译对slab内存管理源代码的说明

750 gpoer

751 g_slice_alloc (gsize mem_size)

752 {

753 gsize chunk_size;

754 gpoer mem;

755 gu acat;

// 对要分配内存大小通过宏P2ALIGN变为以P2ALIGNMENT字节对齐大小

// 并把它赋给chunk_size

756 chunk_size = P2ALIGN (mem_size);

……………

772 g_mutex_lock (allocator->slab_mutex);

773 mem = slab_allocator_alloc_chunk (chunk_size);

774 g_mutex_unlock (allocator->slab_mutex);

……………

778 mem;

779 }

slab翻译对slab内存管理源代码的说明

977 gpoer

978 slab_allocator_alloc_chunk (gsize chunk_size)

979 {

980 ChunkLink *chunk;

// 求SlabInfo指针(allocator->slab_stack)下标

// 也即找到chunk_size对应SlabInfo双向循环链表

981 gu ix = SLAB_INDEX (allocator, chunk_size);

982 /* ensure non-empty slab */

// 判断chunk_size对应SlabInfo双向循环链表循环是否还未建立或是

// 当前SlabInfo中chunk是否已被分配完

// 如果两者任何个成立那么就重新建立个新SlabInfo并把

// 当前SlabInfo指针allocator->slab_stack[ix]指向新建SlabInfo新建

// SlabInfo包含了新分配chunk链表这功能在allocator_add_slab完成

983 (!allocator->slab_stack[ix] || !allocator->slab_stack[ix]->chunks)

984 allocator_add_slab (allocator, ix, chunk_size);

985 /* allocate chunk */

986 chunk = allocator->slab_stack[ix]->chunks;

slab翻译对slab内存管理源代码的说明

// 让被分配chunk脱离chunk链表

987 allocator->slab_stack[ix]->chunks = chunk->next;

988 allocator->slab_stack[ix]->n_allocated;

989 /* rotate empty slabs */

// 如果当前SlabInfochunk已分配完就让当前SlabInfo指针指

// 向下个SlabInfo

990 (!allocator->slab_stack[ix]->chunks)

991 allocator->slab_stack[ix] = allocator->slab_stack[ix]->next;

992 chunk;

993 }

// 这代码分析也可以直接看下面图A解释或是两者结合起来理解

926 void

927 allocator_add_slab (Allocator *allocator,

928 gu ix,

929 gsize chunk_size)

930 {

slab翻译对slab内存管理源代码的说明

931 ChunkLink *chunk;

932 SlabInfo *sinfo;

933 gsize addr, padding, n_chunks, color = 0;

934 gsize page_size = allocator_aligned_page_size (allocator, SLAB_BPAGE_SIZE (allocator,

chunk_size));

935 /* allocate 1 page for the chunks and the slab */

/* 分配页内存给slab和chunk链表 */

936 gpoer aligned_memory = allocator_memalign (page_size, page_size -

NATIVE_MALLOC_PADDING);

937 gu8 *mem = aligned_memory;

938 gu i;

……………

952 /* basic slab info up */

// 把SlabInfo结构信息放在刚分配页内存高地址处

953 sinfo = (SlabInfo*) (mem + page_size - SLAB_INFO_SIZE);

954 sinfo->n_allocated = 0;

955 sinfo->chunks = NULL;

956 /* figure cache colorization */

// 计算这页内存能够划分成多少(n_chunks)个chunk

slab翻译对slab内存管理源代码的说明

957 n_chunks = ((gu8*) sinfo - mem) / chunk_size;

// 再判断是否还有剩余空间padding如果有另作他用

958 padding = ((gu8*) sinfo - mem) - n_chunks * chunk_size;

959 (padding)

960 {

961 color = (allocator->color_accu * P2ALIGNMENT) % padding;

962 allocator->color_accu allocator->config.color_increment;

963 }

964 /* add chunks to free list */

// 找出chunk链表表头

965 chunk = (ChunkLink*) (mem + color);

966 sinfo->chunks = chunk;

// 循环构建chunk链表:把地址相邻chunk链接起来

967 for (i = 0; i < n_chunks - 1; i)

968 {

// 当前chunk指向下个chunk

// (chunk + chunk_size)是下个chunk起始地址

slab翻译对slab内存管理源代码的说明

969 chunk->next = (ChunkLink*) ((gu8*) chunk + chunk_size);

970 chunk = chunk->next;

971 }

// 最后个chunk指向NULL

972 chunk->next = NULL; /* last chunk */

973 /* add slab to slab ring */

974 allocator_slab_stack_push (allocator, ix, sinfo);

975 }

// 功能是根据SlabInfo指针(allocator->slab_stack)下标ix

// 把新建SlabInfo链入对应SlabInfo双向循环链表并把当前SlabInfo指针

// allocator->slab_stack[ix]指向新建SlabInfo

896 allocator_slab_stack_push (Allocator *allocator,

897 gu ix,

898 SlabInfo *sinfo)

899 {

900 /* insert slab at slab ring head */

slab翻译对slab内存管理源代码的说明

901 (!allocator->slab_stack[ix])

902 {

903 sinfo->next = sinfo;

904 sinfo->prev = sinfo;

905 }

906

907 {

908 SlabInfo *next = allocator->slab_stack[ix], *prev = next->prev;

909 next->prev = sinfo;

910 prev->next = sinfo;

911 sinfo->next = next;

912 sinfo->prev = prev;

913 }

914 allocator->slab_stack[ix] = sinfo;

915 }

[Page]对于以上代码重点对allocator_add_slab进行更为详细分析它功能主要是申请页内存用这内存新建立个

slab翻译对slab内存管理源代码的说明

SlabInfo并把它链入对应SlabInfo双向循环链表对于新建立SlabInfo几乎所有跟它相关内部信息都在申请那页内存上:

\">

现在结合上图展开介绍说明代码936、937行申请页内存并把起始地址给mem变量代码953行把页面高地址分给了SlabInfo结构如果有padding话代码958到963是把padding另作它用而上图color大小和空白大小相加就是padding值了这点细节也可以不用太多关注它chunk链表起始地址即链表表头在代码965行确定而上图SlabInfo有个指向chunk链表头指针是在代码966行实现图中chunk链表建立是在代码967到972实现

allocator分配器回收内存chunk:

分配器对内存分配和回收就很简单了通过

g_slice_free1了slab_allocator_free_chunk下面仅对slab_allocator_free_chunk分析:

// 参数中mem就是要释放回收内存chunk

996 slab_allocator_free_chunk (gsize chunk_size,

997 gpoer mem)

998 {

999 ChunkLink *chunk;

1000 gboolean was_empty;

1001 gu ix = SLAB_INDEX (allocator, chunk_size);

1002 gsize page_size = allocator_aligned_page_size (allocator, SLAB_BPAGE_SIZE (allocator,chunk_size));

// 这是求mem所在页面起始地址因地址在逻辑中是扁平线性

// 所以(mem / page_size)就是mem所属是第几个页面那么它乘上page_size就是

slab翻译对slab内存管理源代码的说明

// mem所在页面起始地址

1003 gsize addr = ((gsize) mem / page_size) * page_size;

1004 /* mask page adress */

1005 gu8 *page = (gu8*) addr;

// 获取管理memSlabInfo指针在上面已提到过SlabInfo是放在个页面

// 高地址处

1006 SlabInfo *sinfo = (SlabInfo*) (page + page_size - SLAB_INFO_SIZE);

1007 /* assert valid chunk count */

1008 mem_assert (sinfo->n_allocated > 0);

1009 /* add chunk to free list */

1010 was_empty = sinfo->chunks NULL;

1011 chunk = (ChunkLink*) mem;

// 把要回收chunk链入SlabInfo管理chunk链表表头

1012 chunk->next = sinfo->chunks;

1013 sinfo->chunks = chunk;

1014 sinfo->n_allocated--;

1015 /* keep slab ring partially sorted, empty slabs at end */

slab翻译对slab内存管理源代码的说明

// was_empty为TRUE表明管理要回收chunkSlabInfo所在SlabInfo双向循环链表

// 中每个SlabInfo都可能已把它自己chunk分配完即它们都没有空间可分配了

// 那么就应该把当前SlabInfo改为这次回收了内存chunkSlabInfo以备下次分配用

1016 (was_empty)

1017 {

1018 /* unlink slab */

1019 SlabInfo *next = sinfo->next, *prev = sinfo->prev;

1020 next->prev = prev;

1021 prev->next = next;

1022 (allocator->slab_stack[ix] sinfo)

1023 allocator->slab_stack[ix] = next sinfo ? NULL : next;

1024 /* insert slab at head */

// 重新把SlabInfo链入SlabInfo双向循环链表为是把当

// 前SlabInfo(allocator->slab_stack[ix])改为这次回收了内存chunkSlabInfo

1025 allocator_slab_stack_push (allocator, ix, sinfo);

1026 }

1027 /* eagerly free complete unused slabs */

slab翻译对slab内存管理源代码的说明

1028 (!sinfo->n_allocated)

1029 {

1030 /* unlink slab */

1031 SlabInfo *next = sinfo->next, *prev = sinfo->prev;

1032 next->prev = prev;

1033 prev->next = next;

1034 (allocator->slab_stack[ix] sinfo)

1035 allocator->slab_stack[ix] = next sinfo ? NULL : next;

1036 /* free slab */

// allocator_memfree功能是系统回收内存空间

1037 allocator_memfree (page_size, page);

1038 }

1039 }

到此已把[Page]slab相关代码已分析完比slab更适合于多CPU/多线程magazine内存管理机制比slab更复杂但也更有用更有意思我将在下篇给出

2009-2-12 3:39:11

疯狂代码 /

本文来源:https://www.bwwdw.com/article/ky61.html

Top