1. 程式人生 > >Linux C記憶體池的實現

Linux C記憶體池的實現

在程式設計中,為了避免由於頻繁的malloc/free產生記憶體碎片,通常會在程式中實現自己的記憶體管理模組,即記憶體池。記憶體池的原理:程式啟動時為記憶體池申請一塊較大的記憶體,在程式中使用記憶體時,都由記憶體池進行分配,不再使用的記憶體交給記憶體池回收,用於再次分配。記憶體池一般會有如下的介面:memory_pool_init, memory_pool_malloc, memory_pool_free 和 memory_pool_destroy。本文實現了一個簡單的記憶體池,僅用於基礎學習。

memory_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

memory_pool.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "memory_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_talbe_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 p;
}

//獲得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;
}

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;
}

Makefile:
all:
	gcc main.c memory_pool.c -g
結果:
memory_pool_init: total size: 1024, block cnt: 16, block size: 64
Alloc size: 8, Block: (start: 0, end: 0, cnt: 1)
Malloc success
Block: free: start: 0, end: 0, cnt: 1
Alloc size: 8, Block: (start: 0, end: 0, cnt: 1)
Malloc success
Block: free: start: 0, end: 0, cnt: 1
Alloc size: 256, Block: (start: 0, end: 3, cnt: 4)
Malloc success
Alloc size: 512, Block: (start: 4, end: 11, cnt: 8)
Malloc success
Block: free: start: 0, end: 3, cnt: 4
Alloc size: 256, Block: (start: 0, end: 3, cnt: 4)
Malloc success

記憶體池結構:

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

++ Memory_Pool +++ Memory_Map_Table +++ Memory_Alloc_Table +++ Memory++

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Memory_Pool: 用於記憶體屬性結構,用於整個記憶體池的管理

Memory_Map_Table: 記憶體對映表,是Memory的對映,用於管理Memory

Memory_Alloc_Table: 記憶體分配表,用於記錄分配過的記憶體

Memory:實際用於分配的記憶體

說明:記憶體的分配是以塊為單位的,向上取整,不足一塊的按一塊處理。

缺點:

1.  實際的可分配記憶體只是記憶體池中Memory表示的結構,其它的結構都是記憶體池管理用的,會造成記憶體浪費,而且是記憶體池越大,管理結構越大,浪費記憶體越多

2. 如果塊大小設為64B, 而申請1B,會造成63B的浪費,所以申請記憶體最好是塊大小的整數倍。

3.  當無記憶體可分配時,記憶體池不會自動增長