nginx内存池代码移植

通过对nginx内存池源码的学习,学习到了nginx内存池的精妙处理以及对于代码的运用。由于nginx是通过C语言实现的,本篇文章将使用C++对内存池进行一个简单的封装。

源码移植

头文件

因为本篇博客着重与内存池的思想,故将源码中的日志去掉。

类型定义
// C++11提供的新的类型定义
using u_char = unsigned char;
using ngx_uint_t = unsigned int;

// 类型声明
struct ngx_pool_s;
struct ngx_pool_large_s;
struct ngx_pool_cleanup_s;


// 小块内存的头信息
struct ngx_pool_data_t 
{
	u_char               *last;
	u_char               *end;
	ngx_pool_s           *next;
	ngx_uint_t           failed;
};

// 内存池的信息
struct ngx_pool_s 
{
	ngx_pool_data_t       d;
	size_t                max;
	ngx_pool_s           *current;
	ngx_pool_large_s     *large;
	ngx_pool_cleanup_s   *cleanup;
};

// 大块内存类型
struct ngx_pool_large_s 
{
	ngx_pool_large_s     *next;
	void                 *alloc;
};

// 大块内存释放之前可以执行的回调操作,释放占用的外部资源
using ngx_pool_cleanup_pt = void(*)(void *data);

// 大块内存清除器的类型
struct ngx_pool_cleanup_s 
{
	ngx_pool_cleanup_pt   handler;
	void                 *data;
	ngx_pool_cleanup_s   *next;
};

此次移植变量名和源代码中的变量名均相同,因为C++的语言特性,结构体变量在使用时不需要添加struct关键字,故不采用类型重定义。

辅助函数以及全局变量
/*
**辅助函数
*/
// 把d调整为a的倍数
#define ngx_align(d, a)       (((d) + (a - 1)) & ~(a - 1))

// 小块内存分配考虑字节对齐时的单位
#define NGX_ALIGNMENT		  sizeof(unsigned long) 

// 将p指针按a对齐(将p调整为a的临近的倍数)
#define ngx_align_ptr(p, a)   								\
		(u_char *) (((uintptr_t)(p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))


/*
**全局变量
*/
const int ngx_pagesize = 4096;							// 一个物理页面的大小 4K
const int NGX_MAX_ALLOC_FROM_POOL = ngx_pagesize - 1;	// 可从内存池申请的最大内存
const int NGX_DEFAULT_POOL_SIZE = 16 * 1024;			// 默认内存池的大小 16K
const int NGX_POOL_ALIGNMENT = 16;						// 内存池大小按16字节对齐
const int NGX_MIN_POOL_SIZE =							// 内存池最小的大小跳针为NGX_POOL_ALIGNMENT的倍数
	ngx_align((sizeof(ngx_pool_s) + 2 * sizeof(ngx_pool_large_s)), NGX_POOL_ALIGNMENT);

以上的函数以及变量的含义在代码中已经给出,详情可见nginx内存池源码刨析

内存池类
class m_ngx_pool
{
public:
	m_ngx_pool(size_t size = NGX_DEFAULT_POOL_SIZE);		// 创建内存池
	~m_ngx_pool();											// 销毁内存池
	void ngx_reset_pool();									// 内存重置函数

	void *ngx_palloc(size_t size);							// 内存开辟
	void *ngx_pnalloc(size_t size);							// 内存开辟,不考虑对齐
	void *ngx_pcalloc(size_t size);							// 内存开辟,并初始化为0

	ngx_uint_t ngx_pfree(void *p);							// 内存释放
	ngx_pool_cleanup_s *ngx_pool_cleanup_add(size_t size);	// 添加清理回调操作函数

private:
	ngx_pool_s *pool;										// 指向nginx内存池的入口指针

	void *ngx_palloc_small(size_t size, ngx_uint_t align);	// 小块内存分配
	void *ngx_palloc_block(size_t size);					// 分配新的小块内存池
	void *ngx_palloc_large(size_t size);					// 大块内存分配
};

本项目将内存池的主要函数都封装到了m_ngx_pool这个类中,并依据类的特性,构造和析构函数的功能为创建和销毁内存池。

源文件

创建内存池
m_ngx_pool::m_ngx_pool(size_t size)
{
	// 首先申请size大小的空间
	pool = (ngx_pool_s  *)malloc(size);
	if (pool == nullptr) 
	{
		return;
	}

	// 对内存池的成员初始化
	pool->d.last = (u_char *)pool + sizeof(ngx_pool_s);
	pool->d.end = (u_char *)pool + size;
	pool->d.next = nullptr;
	pool->d.failed = 0;
	
	// 判断该内存池可申请的最大空间
	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;
}
销毁内存池
m_ngx_pool::~m_ngx_pool()
{
	// 调用外部清理函数
	for (ngx_pool_cleanup_s *c = pool->cleanup; c; c = c->next) 
	{
		if (c->handler) 
		{
			c->handler(c->data);
		}
	}

	// 释放大块内存
	for (ngx_pool_large_s *l = pool->large; l; l = l->next) 
	{
		if (l->alloc) 
		{
			free(l->alloc);
		}
	}

	// 释放内存池
	for (ngx_pool_s *p = pool, *n = pool->d.next; ; p = n, n = n->d.next)
	{
		free(p);

		if (n == nullptr) 
		{
			break;
		}
	}
}
内存重置函数
void m_ngx_pool::ngx_reset_pool()
{
	// 首先将大块内存都释放掉
	for (ngx_pool_large_s *l = pool->large; l; l = l->next) 
	{
		if (l->alloc) 
		{
			free(l->alloc);
		}
	}

	// 处理第一块内存池
	ngx_pool_s *p = pool;
	p->d.last = (u_char *) p + sizeof(ngx_pool_s);
	p->d.failed = 0;

	// 第二块内存池到最后一块
	for (p = p->d.next; p; p = p->d.next) 
	{
		p->d.last = (u_char *) p + sizeof(ngx_pool_data_t);
		p->d.failed = 0;
	}

	pool->current = pool;
	pool->large = nullptr;
}
内存开辟
// 内存开辟
void *m_ngx_pool::ngx_palloc(size_t size)
{
	// 小块内存
	if (size <= pool->max) 
	{
		return ngx_palloc_small(size, 1);
	}

	// 大块内存
	return ngx_palloc_large(size);
}

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

	// 大块内存
	return ngx_palloc_large(size);
}

// 内存开辟,并初始化为0
void *m_ngx_pool::ngx_pcalloc(size_t size)
{
	// 先通过ngx_palloc申请size大小
	void *p = ngx_palloc(size);
	if (p) 
	{
		// 对内存进行初始化
		memset(p, 0, size);
	}

	return p;
}
内存释放(大块内存)
ngx_uint_t m_ngx_pool::ngx_pfree(void *p)
{
	// 寻找到对应的大块内存,将其释放掉
	for (ngx_pool_large_s *l = pool->large; l; l = l->next) 
	{
		if (p == l->alloc) 
		{
			free(l->alloc);
			l->alloc = nullptr;

			return 0;
		}
	}

	return -1;
}
添加清理回调操作函数
ngx_pool_cleanup_s *m_ngx_pool::ngx_pool_cleanup_add(size_t size)
{
	// 先申请清理回调函数的内存头
	ngx_pool_cleanup_s *c = (ngx_pool_cleanup_s *)ngx_palloc(sizeof(ngx_pool_cleanup_s));
	if (c == nullptr) 
	{
		return nullptr;
	}

	// 判断size是否为0,不为0则为data申请相应的大小
	if (size) 
	{
		c->data = ngx_palloc(size);
		if (c->data == nullptr)
		{
			return nullptr;
		}
	}
	else 
	{
		c->data = nullptr;
	}

	// 将回调函数指针置空并头插至pool->cleanup
	c->handler = nullptr;
	c->next = pool->cleanup;
	pool->cleanup = c;

	return c;
}
小块内存分配
void *m_ngx_pool::ngx_palloc_small(size_t size, ngx_uint_t align)
{
	ngx_pool_s *p = pool->current;	// 指向内存池申请空间的入口地址

	// 遍历现有的内存池
	do 
	{
		u_char *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 *m_ngx_pool::ngx_palloc_block(size_t size)
{
	size_t psize = (size_t)(pool->d.end - (u_char *)pool);

	// 申请一块和之前内存池大小相当的空间
	u_char *m = (u_char *)malloc(psize);
	if (m == nullptr) 
	{
		return nullptr;
	}

	// 将空间类型转为ngx_pool_s并进行初始化
	ngx_pool_s  *newp = (ngx_pool_s *)m;
	newp->d.end = m + psize;
	newp->d.next = nullptr;
	newp->d.failed = 0;

	// 为用户分配空间
	m += sizeof(ngx_pool_data_t);
	m = ngx_align_ptr(m, NGX_ALIGNMENT);
	newp->d.last = m + size;

	// 调整内存池申请空间的入口
	ngx_pool_s *p = pool->current;
	for (; p->d.next; p = p->d.next) 
	{
		if (p->d.failed++ > 4) 
		{
			pool->current = p->d.next;
		}
	}

	p->d.next = newp;

	return m;
}
大块内存分配
void *m_ngx_pool::ngx_palloc_large(size_t size)
{
	// 首先申请大块内存
	void *p = malloc(size);
	if (p == nullptr) 
	{
		return nullptr;
	}

	// 遍历前三个大块内存头,看有没有闲置的内存头
	ngx_uint_t n = 0;
	ngx_pool_large_s *large = pool->large;

	for (; large; large = large->next) 
	{
		if (large->alloc == nullptr) 
		{
			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;
}

以上就是此次内存池代码移植后的所有源码,相较于源码,避免了指针定义时未初始化的问题,也对一些不足之处稍加改进

测试代码

struct Test
{
	char *ptr;
};

void clearptr(void *p)
{
	char *ptr = (char*)p;
	free(ptr);
	cout << "free ptr: \"I Love You, Darling\" " << endl;
}

int main()
{
	m_ngx_pool pool(512);

	// 从小块内存池分配的
	void *p1 = pool.ngx_palloc(128);
	if (p1 != nullptr)
	{
		cout << "small ngx_palloc success" << endl;
	}
	else
	{
		cout << "small ngx_palloc fail" << endl;
		return -1;
	}

	// 从大块内存池分配的
	Test *p2 = (Test*)pool.ngx_palloc(512);
	if (p2 != nullptr)
	{
		cout << "large ngx_palloc success" << endl;
	}
	else
	{
		cout << "large ngx_palloc fail" << endl;
		return -1;
	}
	p2->ptr = (char*)malloc(30);
	strcpy_s(p2->ptr, 21, "I Love You, Darling");

	ngx_pool_cleanup_s *c = pool.ngx_pool_cleanup_add(sizeof(char*));
	c->handler = clearptr;
	c->data = p2->ptr;

	return 0;
}

这个测试代码测试了内存池小块内存开辟,大块内存的开辟以及外部清理函数,运行的结果如下:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值