04_19linux自己撸内存池实战,仿造slab分配器

前言

自己撸一个内存池 其实就相当于linux里面带的 slab分配器 可以翻翻之前的章
看看slab 和伙伴分配器的不同
在学习c语言时,我们常常会使用到malloc()去申请一块内存空间,用于存放我们的数据。刚开始我们只要知道申请内存时使用用malloc去申请一块就可以,而其中的原理我们并不关心。但是随着我们对运行环境的要求越来越多样化,复杂化,以及对稳定性以及性能问题的要求逐渐变得越来越重要时,我们往往需要关注到性能问题。而研究性能问问,如果只停留在知道使用malloc()可以去申请一块内存空间是远远不够的,此时我们就需要去研究相关的原理和代码。
在Linux中,最先推出用于分配内存的管理单元和算法是伙伴分配器 伙伴分配器是以页为单位管理和分配内存。但是伙伴分配器要求所有分配单元是2的幂,而在内核中的需求却以字节为单位(在内核中面临频繁的结构体内存分配问题)。假如我们需要动态申请一个内核结构体(占 20 字节),若仍然分配一页内存,这将严重浪费内存。这也将会导致内部碎片,也就是我们常说的内存碎片问题。那么该如何分配才能减少内存碎片呢?
slab 分配器专为小内存分配而生,slab分配器分配内存以字节为单位,基于伙伴分配器的大内存进一步细分成小内存分配。换句话说,slab 分配器仍然从 Buddy 分配器中申请内存,之后自己对申请来的内存细分管理。从而达到减少内存碎片化的目的。
我们可以把伙伴分配器理解成一个仓库,它提供给商店(slab)进行批发的货物,商店(slab)从仓库(伙伴分配器)进货以后,再零售给消费者(使用kmalloc的用户)。

使用命令 可以看见slab分配器的情况 有很多预先分配
root@test-PC:~#  cat /proc/slabinfo

内存池原理

memory pool是- -种内存分配方式,又被称为固定大小区块规划。平时我们直接所使用
的malloc,new,free,delete等等API申请内存分配,这做缺点在于,由于所申请内存块的
大小不定,当频繁使用时会造成大量的内存碎片并进而降低性能。
内存池则是在真正使用内存之前,先申请分配一 定数量的、大小相等的内存块留作备
用。当有新的内存需要的时候,就直接从内存池中分出一 部分内存块,若内存块不够再
继续申请新的内存,这样做优势,使得内存分配效率得到提升。

手撸内存池

可以理解为在用户空间提前分配内存
main函数模拟你的app 在app中使用自定义的 函数进行malloc 和内存释放

要做的

1、获取内存映射表的位置
2、获取内存所在位置
3、获取内存分配表的位置
4、内存池初始化
5、内存分配
6、内存释放
7、内存池销毁

main.c

#include 
 
#include "memory_pool.h"
 
 
#define LOOP 5
#define ALLOC_SIZE 8
 
int main(void)
{
	Memory_Pool *pool = NULL;
	char *p1 = NULL;
	char *p2 = NULL;
	int i = 0;
 
	pool = memory_pool_init(1024, 512);
	if (pool == NULL)
		printf("memory pool init failed\n");
 
	for (i = 0; i < 2; i++) {
		p1 = (char *)Memory_malloc(pool, ALLOC_SIZE);
		if (p1 == NULL)
			printf("Malloc failed\n");
		else
			printf("Malloc success\n");
 
		memory_free(pool, p1);
	}
 
 
	p1 = (char *)Memory_malloc(pool, 256);
	if (p1 == NULL)
		printf("Malloc failed\n");
	else
		printf("Malloc success\n");
 
	p2 = (char *)Memory_malloc(pool, 512);
	if (p1 == NULL)
		printf("Malloc failed\n");
	else
		printf("Malloc success\n");
 
	memory_free(pool, p1);
 
	p1 = (char *)Memory_malloc(pool, 256);
	if (p1 == NULL)
		printf("Malloc failed\n");
	else
		printf("Malloc success\n");
 
	memory_pool_destroy(pool);
 
	return 0;
}
linux_pool.h
#ifndef __MEMORY_POOL_H__
#define __MEMORY_POOL_H__
 
#define MAX_POOL_SIZE 1024 * 1024
#define BLOCK_SIZE 64
 
//内存映射表对应的数据类型
typedef struct memory_map_talbe
{
	char *p_block;
	int index;
	int used;
} Memory_Map_Table;


//内存分配表 
typedef struct memory_alloc_table
{
	char *p_start;
	int used;
	int block_start_index;
	int block_cnt;
}Memory_Alloc_Table;
 
//内存池类型
typedef struct memory_pool
{
	char *memory_start;//内存池起始地址, free整个内存池时使用
	Memory_Alloc_Table *alloc_table; //指向刚刚的内存映射表
	Memory_Map_Table *map_table;  //指向刚刚的内存分配表
	int total_size;
	int internal_total_size;
	int increment;
	int used_size; //使用大小
	int block_size; //块大小
	int block_cnt; //块数量
	int alloc_cnt; //分配的数量
} Memory_Pool;
 
extern Memory_Pool *memory_pool_init(int size, int increment);
extern void *Memory_malloc(Memory_Pool *pool, int size);
extern void memory_free(Memory_Pool *pool, void *memory);
extern void memory_pool_destroy(Memory_Pool *pool);
 
#endif
 

linux_pool.c
#include 
#include 
#include 
 
#include "linux_pool.h"
//获取内存映射表的位置
Memory_Map_Table *map_table_pos(Memory_Pool *pool)
{
	Memory_Map_Table *p = (Memory_Map_Table *)(pool->memory_start + sizeof(Memory_Pool));
	return p;
}

//获取内存映射表的位置
Memory_Alloc_Table *alloc_table_pos(Memory_Pool *pool)
{
    //感觉这里需要先知道整体架构 才能知道这个位置怎么算出来的
	Memory_Alloc_Table *p = (Memory_Alloc_Table *) \
            (pool->memory_start + sizeof(Memory_Pool)+sizeof(Memory_Map_Table)*(pool->block_cnt));
	return pm;
}

//获得memory在位置
char *memory_pos(Memory_Pool *pool)
{
	char *p = (char *)(pool->memory_start + sizeof(Memory_Pool) +
			(sizeof(Memory_Map_Table) + sizeof(Memory_Alloc_Table))* pool->block_cnt);
	return p;
}
 
Memory_Pool *memory_pool_init(int size, int increment)
{
	char *p = NULL;
	char *p_memory = NULL;
	Memory_Pool *pool = NULL;
	Memory_Alloc_Table *alloc_table = NULL; //内存分配表的地址(首地址)
	Memory_Alloc_Table *p_alloc_table = NULL;//因为有多个表(这个指针结合上的首地址来描述后面的表)
	Memory_Map_Table *map_table = NULL;	
	Memory_Map_Table *p_map_table = NULL;
	int block_cnt = 0;
	int all_size = 0;
	int i = 0;
 
	if (size < 0 || size > MAX_POOL_SIZE) {
		printf("memory_pool_init(): Invalid size(%d)\n", size);
		return pool;
	}
 
	block_cnt = ((size + BLOCK_SIZE - 1) / BLOCK_SIZE);  //这里写的很奇怪 等下看看取整怎么写的
    //算出总体大小 进行分配
	all_size = sizeof(Memory_Pool) + (sizeof(Memory_Map_Table) +
			sizeof(Memory_Alloc_Table)) * block_cnt + size;
	p = (char *)malloc(all_size);
	if (p == NULL) {
		perror("Malloc failed\n");
		return pool;
	}
 
	memset(p, 0, all_size);
 
	pool = (Memory_Pool *)p;
	pool->block_cnt = block_cnt;
	pool->block_size = BLOCK_SIZE;
	pool->increment = increment;
	pool->internal_total_size = BLOCK_SIZE * block_cnt;
	pool->total_size = size;
	pool->used_size = 0;
	pool->alloc_cnt = 0;
	pool->memory_start = p;
 
	p_memory = memory_pos(pool);
	map_table = map_table_pos(pool);
    //每个内存块都应该有个映射表
	for (i = 0; i < block_cnt; i++) {
		p_map_table = (Memory_Map_Table *)((char *)map_table + i * sizeof(Memory_Map_Table));
		p_map_table->index = 0;
		p_map_table->p_block = p_memory + i * BLOCK_SIZE;
		p_map_table->used = 0;
	}
 
	alloc_table = alloc_talbe_pos(pool);
	for (i = 0; i < block_cnt; i++) {
		p_alloc_table = (Memory_Alloc_Table *)((char *)alloc_table + i * sizeof(Memory_Alloc_Table));
		p_alloc_table->block_cnt = 0;
		p_alloc_table->block_start_index = -1;
		p_alloc_table->p_start = NULL;
		p_alloc_table->used = 0;
	}
 
	printf("memory_pool_init: total size: %d, block cnt: %d, block size: %d\n",
			pool->total_size, pool->block_cnt, BLOCK_SIZE);
	return pool;
}
 
//自定义的内存分配函数,使用自己的内存池来分配内存
void *Memory_malloc(Memory_Pool *pool, int size)
{
	char *p_start = NULL;
	int need_block_cnt = 0;
	Memory_Map_Table *map_table = NULL;
	Memory_Map_Table *p_map_table = NULL;
	Memory_Alloc_Table *alloc_table = NULL;
	Memory_Alloc_Table *p_alloc_table = NULL;
	int block_cnt = 0;
	int start_index = -1;
	int i = 0;
 
	if (size <= 0) {
		printf("Invalid size(%d)\n", size);
		return p_start;
	}
 
	if (size > pool->total_size) {
		printf("%d is more than total size\n", size);
		return p_start;
	}
    //剩余的不够
	if (size > pool->total_size - pool->used_size) {
		printf("Free memory(%d) is less than allocate(%d)\n",
		pool->total_size - pool->used_size, size);
		return NULL;
	}
 
	need_block_cnt = (size + BLOCK_SIZE - 1) / BLOCK_SIZE;//取整
	map_table = map_table_pos(pool);
 
	start_index = -1;
	for (i = 0; i < pool->block_cnt; i++) {
        //找到一个能用的映射表
		p_map_table = (Memory_Map_Table *)((char *)map_table + i * sizeof(Memory_Map_Table));
		if (p_map_table->used) {
			//printf("before alloc: map index: %d is used\n", i);
			block_cnt = 0;
			start_index = -1;
			continue;
		}
 
		if (start_index == -1) {
			start_index = i;
			//printf("start_index: %d\n", start_index);
		}
		block_cnt++;
 
		if (block_cnt == need_block_cnt) {
			break;
		}
	}
 
	if (start_index == -1) {
		printf("No available memory to used\n");
		return NULL;
	}
    //找当能用的内存分配表
	alloc_table = alloc_talbe_pos(pool);
 
	for (i = 0; i < pool->block_cnt; i++) {
		p_alloc_table = (Memory_Alloc_Table *)((char *)alloc_table + i * sizeof(Memory_Alloc_Table));
		if (p_alloc_table->used == 0) {
			break;
		}
		p_alloc_table = NULL;
	}
 
	if (p_alloc_table == NULL) {
		return NULL;
	}
	p_map_table = (Memory_Map_Table *)((char *)map_table + sizeof(Memory_Map_Table) * start_index);
	p_alloc_table->p_start = p_map_table->p_block;
	p_alloc_table->block_start_index = p_map_table->index;
	p_alloc_table->block_cnt = block_cnt;
	p_alloc_table->used = 1;
 
	//printf("block cnt is %d\n", block_cnt);
	for (i = start_index; i < start_index + block_cnt; i++) {
		p_map_table = (Memory_Map_Table *)((char *)map_table + i * sizeof(Memory_Map_Table));
		//printf("map index: %d is used\n", i);
		p_map_table->used = 1;
	}
 
	printf("Alloc size: %d, Block: (start: %d, end: %d, cnt: %d)\n", size,
			start_index, start_index + block_cnt - 1, block_cnt);
	pool->alloc_cnt++;
	pool->used_size += size;
	return p_alloc_table->p_start;
}
 
void memory_free(Memory_Pool *pool, void *memory)
{
	Memory_Map_Table *map_table = NULL;
	Memory_Map_Table *p_map_table = NULL;
	Memory_Alloc_Table *alloc_table = NULL;
	Memory_Alloc_Table *p_alloc_table = NULL;
	int i = 0;
	int block_start_index = 0;
	int block_cnt = 0;
 
	if (memory == NULL) {
		printf("memory_free(): memory is NULL\n");
		return;
	}
 
	if (pool == NULL) {
		printf("Pool is NULL\n");
		return;
	}
 
	alloc_table = alloc_talbe_pos(pool);
 
	for (i = 0; i < pool->alloc_cnt; i++) {
		p_alloc_table = (Memory_Alloc_Table *)((char *)(alloc_table) + i * sizeof(Memory_Alloc_Table));
		if (p_alloc_table->p_start == memory) {
			block_start_index = p_alloc_table->block_start_index;
			block_cnt = p_alloc_table->block_cnt;
		}
	}
 
	if (block_cnt == 0) {
		return;
	}
 
	map_table = map_table_pos(pool);
 
	printf("Block: free: start: %d, end: %d, cnt: %d\n", block_start_index,
			block_start_index + block_cnt -1, block_cnt);
	for (i = block_start_index; i < block_start_index + block_cnt; i++) {
		p_map_table = (Memory_Map_Table *)((char *)map_table + i * sizeof(Memory_Map_Table));
		p_map_table->used = 0;
	}
 
	p_alloc_table->used = 0;
	pool->used_size -= block_cnt * BLOCK_SIZE;
}
 
void memory_pool_destroy(Memory_Pool *pool)
{
	if (pool == NULL) {
		printf("memory_pool_destroy: pool is NULL\n");
		return;
	}
 
	free(pool);
	pool = NULL;
}

你可能感兴趣的:(狂刷KPI,网络,服务器,运维)