Nginx内存池仿写和测试

安稳与你 提交于 2020-02-20 04:47:29

Nginx内存池仿写和测试

【Nginx内存池源码刨析】
使用C++语言将Nginx内存池封装成面向对象内存池,将内存池的创建和销毁实现在构造函数和析构函数中。

Nginx内存池代码

ngx_mem_pool.h文件

#ifndef NGX_MEM_POOL_H
#define NGX_MEM_POOL_H
#include <cstdlib>
#include <cstring>

// Nginx内存池的主要类型定义
using u_char = unsigned char;
using ngx_uint_t = unsigned int;

// 外部资源释放结构体类型
typedef void(*ngx_pool_cleanup_pt)(void *data); // 回调函数指针
struct ngx_pool_cleanup_s
{
	ngx_pool_cleanup_pt   handler; // 回调函数指针
	void                 *data;    // 传入回调函数的实参
	ngx_pool_cleanup_s   *next;    // 指向下一个外部资源释放结构体类型
};

// 大块内存的头信息
struct ngx_pool_large_s {
	ngx_pool_large_s     *next;  // 指向下一个大块内存的头部
	void                 *alloc; // 指向开辟内存的首地址
};

// 内存池中分配内存的小块内存的内存块头信息
struct ngx_pool_s; // 类型声明
struct ngx_pool_data_s {
	u_char               *last;   // 指向内存块可用内存起始位置
	u_char               *end;    // 指向内存块可用内存末尾位置
	ngx_pool_s           *next;   // 指向下一个内存块
	ngx_uint_t            failed; // 记录当前内存块开辟小块内存失败次数
};

// 内存池的头部信息
struct ngx_pool_s {
	ngx_pool_data_s       d;
	size_t                max;     // 小块内存和大块内存的界限
	ngx_pool_s           *current; // 指向小块内存池中第一块可用内存块
	ngx_pool_large_s     *large;   // 记录大块内存的链表起始地址
	ngx_pool_cleanup_s   *cleanup; // 记录释放外部资源的链表起始地址

	// ngx_chain_t       *chain; 这个暂时不用
	// ngx_log_t         *log;   这个是ngx的日志
};

// 一个物理页面的大小4k
const int ngx_pagesize = 4096;
// ngx定义小块内存最大可分配一个页面 4k
const int NGX_MAX_ALLOC_FROM_POOL = ngx_pagesize - 1; 
// ngx内存池默认大小,16k
const int NGX_DEFAULT_POOL_SIZE = 16 * 1024;
// ngx内存对齐是以16为基准的
const int NGX_POOL_ALIGNMENT = 16;

// 将d的大小提升为a的倍数
#define ngx_align(d, a)  (((d) + (a - 1)) & ~(a - 1))
// ngx内存池最小字节数,因为至少需要存放内存池的头信息
const int NGX_MIN_POOL_SIZE = 
		ngx_align((sizeof(ngx_pool_s) + 2 * sizeof(ngx_pool_large_s)),
			NGX_POOL_ALIGNMENT);
// NGX对齐方式是平台unsigned long
const int NGX_ALIGNMENT = sizeof(unsigned long);

// 将p大小提升为a的倍数
#define ngx_align_ptr(p, a) (u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))

class Ngx_mem_pool
{
public:
	// 将内存池的构造和销毁直接写入构造和析构函数中
	Ngx_mem_pool(size_t size);
	~Ngx_mem_pool();

	// 创建内存池
	//bool ngx_create_pool(size_t size);
	// 释放内存池
	//void ngx_destroy_pool();

	// 重置内存池
	void ngx_reset_pool();

	// 从内存池中开辟内存,并且考虑内存对齐 
	void* ngx_palloc( size_t size);

	// 从内存池中开辟内存,不考虑内存对齐
	void* ngx_pnalloc( size_t size);

	//  从内存池中开辟内存,调用的是ngx_palloc,并初始化为0
	void* ngx_pcalloc( size_t size);

	// 大块内存释放函数
	bool ngx_pfree(void *p);

	// 添加回调清理函数
	ngx_pool_cleanup_s * ngx_pool_cleanup_add(size_t size);
private:
	// 小块内存分配
	void * ngx_palloc_small(size_t size, ngx_uint_t align);
	
	// 大块内存分配
	void* ngx_palloc_large(size_t size);

	// 分配小块内存块函数
	void* ngx_palloc_block(size_t size);

	// 开辟内存函数,调用malloc
	void* ngx_alloc(size_t size);

	ngx_pool_s *pool;
};


#endif // !NGX_MEM_POOL_H

ngx_mem_pool.cpp文件

#include "ngx_mem_pool.h"

// 创建内存池
Ngx_mem_pool::Ngx_mem_pool(size_t size)
{
	// 调用malloc开辟内存
	pool =(ngx_pool_s*) ngx_alloc(size);
	if (pool == nullptr) 
	{
		throw"mem pool failed!";
	}

	// last指向当前内存块中可用内存起始地址
	pool->d.last = (u_char *)pool + sizeof(ngx_pool_s);
	pool->d.end = (u_char *)pool + size; // end指向当前内存块末尾地址
	pool->d.next = nullptr;
	pool->d.failed = 0;

	// size 如果小于4k,max = size 否则 max = 4095
	size = size - sizeof(ngx_pool_s);
	pool->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;

	pool->current = pool;
	pool->large = nullptr;
	pool->cleanup = nullptr;
	// p->chain = NULL;
	// p->log = log;
}

// 释放内存池
Ngx_mem_pool::~Ngx_mem_pool()
{
	ngx_pool_s          *p, *n;
	ngx_pool_large_s    *l;
	ngx_pool_cleanup_s  *c;

	// 遍历外部资源的头部,调用回调函数释放外部资源
	for (c = pool->cleanup; c != nullptr; c = c->next) 
	{
		if (c->handler != nullptr) 
		{
			c->handler(c->data);
		}
	}
	// 遍历大块内存头信息,释放大块内存
	for (l = pool->large; l != nullptr; l = l->next) 
	{
		if (l->alloc != nullptr) 
		{
			free(l->alloc);
		}
	}
	// 遍历整个内存池,释放所有小块内存内存块
	for (p = pool, n = pool->d.next;; p = n, n = n->d.next)
	{
		free(p);
		if (n == NULL) 
		{
			break;
		}
	}
}

// 重置内存池
void Ngx_mem_pool::ngx_reset_pool()
{
	ngx_pool_s        *p;
	ngx_pool_large_s  *l;

	for (l = pool->large; l != nullptr; l = l->next) 
	{
		if (l->alloc != nullptr) 
		{
			free(l->alloc);
		}
	}

	/*
	for (p = pool; p != nullptr; p = p->d.next) 
	{
		p->d.last = (u_char *)p + sizeof(ngx_pool_s);
		p->d.failed = 0;
	}
	*/

	// 将内存池中第一个内存块单独重置,因为它的头部信息较多
	p = pool;
	p->d.last = (u_char *)p + sizeof(ngx_pool_s);
	p->d.failed = 0;
	// 重置其他内存块
	for (p = pool->d.next; p != nullptr; p = p->d.next)
	{
		p->d.last = (u_char *)p + sizeof(ngx_pool_data_s);
		p->d.failed = 0;
	}

	pool->current = pool;
	pool->large = nullptr;
	//pool->chain = NULL;
}

// 从内存池中开辟内存,并且考虑内存对齐 
void* Ngx_mem_pool::ngx_palloc(size_t size)
{
	if (size <= pool->max)// 小块内存
	{
		return ngx_palloc_small(size, 1);
	}
	// 大块内存
	return ngx_palloc_large(size);
}

// 从内存池中开辟内存,不考虑内存对齐
void* Ngx_mem_pool::ngx_pnalloc(size_t size)
{
	if (size <= pool->max)// 小块内存
	{
		return ngx_palloc_small(size, 0);
	}
	// 大块内存
	return ngx_palloc_large(size);
}

//  从内存池中开辟内存,调用的是ngx_palloc,并初始化为0
void* Ngx_mem_pool::ngx_pcalloc(size_t size)
{
	void *p;

	p = ngx_palloc(size); // 开辟内存
	
	if (p) 
	{   // 将内存清0 
		memset(p, 0, size);
	}

	return p;
}

// 大块内存释放函数
bool Ngx_mem_pool::ngx_pfree(void *p)
{
	ngx_pool_large_s  *l;

	for (l = pool->large; l != nullptr; l = l->next) 
	{
		if (p == l->alloc) 
		{
			free(l->alloc);
			l->alloc = nullptr;
			return true;
		}
	}
	return false;
}

// 添加回调清理函数
ngx_pool_cleanup_s * Ngx_mem_pool::ngx_pool_cleanup_add(size_t size)
{
	ngx_pool_cleanup_s  *c;

	// 在小块内存池中开辟外部资源结构体
	c = (ngx_pool_cleanup_s*)ngx_palloc(sizeof(ngx_pool_cleanup_s));
	if (c == nullptr)
	{
		return nullptr;
	}

	if (size) 
	{
		c->data = ngx_palloc(size); // 在小块内存中开辟内存存储回调函数实参
		if (c->data == nullptr)
		{
			return nullptr;
		}
	}
	else 
	{
		c->data = nullptr;
	}

	c->handler = nullptr;
	c->next = pool->cleanup;
	pool->cleanup = c;

	return c;
}

// 小块内存分配
void * Ngx_mem_pool::ngx_palloc_small(size_t size, ngx_uint_t align)
{
	u_char      *m;
	ngx_pool_s  *p;

	p = pool->current;

	do {
		m = p->d.last;

		if (align) 
		{
			m = ngx_align_ptr(m, NGX_ALIGNMENT);
		}

		if ((size_t)(p->d.end - m) >= size) 
		{
			p->d.last = m + size;
			return m;
		}

		p = p->d.next;

	} while (p);

	return ngx_palloc_block(size);
}

// 大块内存分配
void* Ngx_mem_pool::ngx_palloc_large(size_t size)
{
	void              *p;
	ngx_uint_t         n;
	ngx_pool_large_s  *large;

	p = ngx_alloc(size);// 调用malloc开辟内存
	if (p == nullptr) 
	{
		return nullptr;
	}
	// 在大块内存头链表的前三个头部查看是否有空闲大块内存头部可用
	n = 0;
	for (large = pool->large; large != nullptr; large = large->next) 
	{
		if (large->alloc == NULL) 
		{
			large->alloc = p;
			return p;
		}

		if (n++ > 3) 
		{
			break;
		}
	}
	// 在小块内存池中开辟大块内存头部
	large = (ngx_pool_large_s*)ngx_palloc_small(sizeof(ngx_pool_large_s), 1);
	if (large == nullptr) 
	{
		free(p);
		return nullptr;
	}

	large->alloc = p;
	large->next = pool->large;
	pool->large = large;

	return p;
}

// 分配小块内存 内存块函数
void* Ngx_mem_pool::ngx_palloc_block(size_t size)
{
	u_char      *m;
	size_t       psize;
	ngx_pool_s  *p, *n;
	
	// 获取第一个内存块大小
	psize = (size_t)(pool->d.end - (u_char *)pool);

	m = (u_char*)ngx_alloc(psize);// 开辟内存
	if (m == nullptr) 
	{
		return nullptr;
	}

	// 初始化新开辟内存的头信息
	n = (ngx_pool_s* )m;
	n->d.end = m + psize;
	n->d.next = nullptr;
	n->d.failed = 0;

	m += sizeof(ngx_pool_data_s);
	m = ngx_align_ptr(m, NGX_ALIGNMENT); // 进行内存对齐
	n->d.last = m + size; // last指向开辟size后可用内存的起始地址

	// 内存开辟失败,然后将failed++
	for (p = pool->current; p->d.next != nullptr; p = p->d.next) 
	{
		if (p->d.failed++ > 4) 
		{
			pool->current = p->d.next;
		}
	}

	p->d.next = n;

	return m;
}

// 开辟内存函数,调用malloc
void* Ngx_mem_pool::ngx_alloc(size_t size)
{
	void *p = malloc(size);
	if (p == nullptr)
		return nullptr;
	return p;
}

测试代码

#include "ngx_mem_pool.h"
#include <iostream>
using namespace std;

struct Node
{
	char*str;
	FILE *file;
};

void func1(void * str)
{
	free(str);
	cout << "free str" << endl;
}
void func2(void *fd)
{
	fclose((FILE*)fd);
	cout << "close file" << endl;
}

int main()
{
	Ngx_mem_pool mempool(512); //创建一个内存池
	// 小块内存
	int *q = (int*)mempool.ngx_palloc(20);
	if (q == nullptr)
	{
		return -1;
	}
	
	// 大块内存
	char *p = (char*)mempool.ngx_palloc(600);
	if (p == nullptr)
	{
		return  -1;
	}

	// 在小内存中开辟一个Node
	Node* node = (Node*)mempool.ngx_palloc(sizeof(Node));
	if (node == nullptr)
	{
		return -1;
	}

	node->str = (char*)malloc(20);
	strcpy(node->str, "hello world Ngx!");
	node->file = fopen("test.txt", "r");
	if (node->file == nullptr)
	{
		return -1;
	}
    
	ngx_pool_cleanup_s* str = mempool.ngx_pool_cleanup_add(sizeof(ngx_pool_cleanup_s));
	str->handler = func1;
	str->data = node->str;

	ngx_pool_cleanup_s*file = mempool.ngx_pool_cleanup_add(sizeof(ngx_pool_cleanup_s));
	file->handler = func2;
	file->data = node->file;


	return 0;
}

测试代码可以通过单步调试,会看到内存池的实现!


其他修改注释等都在代码中有详细注释,新手上路,如有错误,请指出!!!

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!