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

前言

自己撸一个内存池 其实就相当于linux里面带的 slab分配器 可以翻翻之前的章

看看slab 和伙伴分配器的不同

在学习c语言时,我们常常会使用到malloc()去申请一块内存空间,用于存放我们的数据。刚开始我们只要知道申请内存时使用用malloc去申请一块就可以,而其中的原理我们并不关心。但是随着我们对运行环境的要求越来越多样化,复杂化,以及对稳定性以及性能问题的要求逐渐变得越来越重要时,我们往往需要关注到性能问题。而研究性能问问,如果只停留在知道使用malloc()可以去申请一块内存空间是远远不够的,此时我们就需要去研究相关的原理和代码。

在Linux中,最先推出用于分配内存的管理单元和算法是伙伴分配器 伙伴分配器是以页为单位管理和分配内存。但是伙伴分配器要求所有分配单元是2的幂,而在内核中的需求却以字节为单位(在内核中面临频繁的结构体内存分配问题)。假如我们需要动态申请一个内核结构体(占 20 字节),若仍然分配一页内存,这将严重浪费内存。这也将会导致内部碎片,也就是我们常说的内存碎片问题。那么该如何分配才能减少内存碎片呢?

slab 分配器专为小内存分配而生,slab分配器分配内存以字节为单位,基于伙伴分配器的大内存进一步细分成小内存分配。换句话说,slab 分配器仍然从 Buddy 分配器中申请内存,之后自己对申请来的内存细分管理。从而达到减少内存碎片化的目的。

我们可以把伙伴分配器理解成一个仓库,它提供给商店(slab)进行批发的货物,商店(slab)从仓库(伙伴分配器)进货以后,再零售给消费者(使用kmalloc的用户)。

bash 复制代码
使用命令 可以看见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、内存池销毁

c 复制代码
main.c

#include <stdio.h>
 
#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;
}
c 复制代码
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
 
c 复制代码
linux_pool.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#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;
}
相关推荐
@兽血沸腾7 分钟前
Windows系统安装Jenkins部署应用至远程Windows服务器操作指南
运维·服务器·jenkins
承渊政道3 小时前
Linux网络学习【Reactor反应堆模式】
linux·网络·学习·ubuntu·reactor
讯捷蓝达5 小时前
戴尔R720xd服务器卡死在 “Configuring Memory...Done“,换了内存还是卡?问题根本不在内存
运维·服务器
数字新视界8 小时前
动环监控信创适配在智能机房管理中的应用价值与未来展望
大数据·服务器·数据库·动环监控系统·动力与环境监控系统
爱和冰阔落8 小时前
【Linux网络】TCP 服务器如何并发处理多个客户端?从 fork、僵尸进程到双重 fork
linux·运维·服务器·网络
lisanmengmeng8 小时前
alertmanger 标签 rules 打上分组标签
linux·运维·服务器
严肃小白兔9 小时前
Cloudflare 更新 API Token 管理:从最小权限到自动化凭据安全
网络·安全·自动化
Android系统攻城狮9 小时前
Linux FFmpeg深度解析之av_write_frame调用流程与实战(三十三)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频
上火的金鱼妹9 小时前
k8s生产实际问题排查(网关,数据库读写等)
运维·服务器
孙克旭_9 小时前
k8s基础组件与创建资源
运维·docker·容器·kubernetes