手写移植Nginx内存池
继上篇的剖析Nginx内存池源码剖析
这篇开始手写移植Nginx内存池
ngx_mem_pool.h
#pragma once
#include <cstdlib>
#include <memory>
/*
移植nginx内存池的代码,用OOP实现
*/
//类型重定义
using u_char = unsigned char;
using ngx_uint_t = unsigned int;
//类型前置声明
struct ngx_pool_s;
//清理函数(回调函数)的类型
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;//所有的cleanup清理操作都被穿在一个链表上
};
/*大块内存的头部信息*/
struct ngx_pool_large_s {
ngx_pool_large_s* next;//所有大块内u你分配都是被穿在一个链表上
void* alloc;//保存大块内存的起始地址
};
/*分配小块内存的内存池的头部数据信息*/
struct ngx_pool_data_t {
u_char* last;//小块可用的起始地址
u_char* end;//小块内存的末尾地址
ngx_pool_s* next;//所有小块内存池都被穿在一个链表
ngx_uint_t failed;//记录小块内存池分配内存的失败次数
} ;
/*nginx内存池的头部信息和管理成员信息*/
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;//指向所有预制的清理操作的回调函数链表
};
/*
* NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86.
* On Windows NT it decreases a number of locked pages in a kernel.
*/
//buf缓冲区清零
#define ngx_memzero(buf, n) (void) memset(buf, 0, n)
//把数值d调整到临界a的倍数
#define ngx_align(d, a) (((d) + (a - 1)) & ~(a - 1))
//把指针p调整到a的临近倍数
#define ngx_align_ptr(p, a) \
(u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))
//默认一个物理页面是4k
const int ngx_pagesize = 4096;
//ngx小块内存池可分配的最大空间
const int NGX_MAX_ALLOC_FROM_POOL = (ngx_pagesize - 1);
//表示一个默认的ngx内存池开辟的大小
const int NGX_DEFAULT_POOL_SIZE = (16 * 1024);//16k
//内存池大小按照16字节进行对齐
const int NGX_POOL_ALIGNMENT = 16;
//ngx小块内存池最小的size调整成ngx_pool_alignment的临近倍数
const int NGX_MIN_POOL_SIZE =
ngx_align((sizeof(ngx_pool_s) + 2 * sizeof(ngx_pool_large_s)),
NGX_POOL_ALIGNMENT);
class ngx_mem_pool
{
public:
//创建指定size代销的内存池,但是小块内存池不超过一个页面的大小
void* ngx_create_pool(size_t size);
//考虑内存池字节对齐,从内存池申请size大小的内存
void* ngx_palloc( size_t size);
//和上面函数一样,不考虑内存字节的对齐
void* ngx_pnalloc( size_t size);
//调用的是ngx_palloc实现内存分配,但是会初始化为0
void* ngx_pcalloc( size_t size);
//内存池释放
void ngx_pfree( void* p);
//内存池销毁
void ngx_destroy_pool();
//内存池重置
void ngx_reset_pool();
//回调清理
ngx_pool_cleanup_s* ngx_pool_cleanup_add( size_t size);
//小块内存的分配
#define NGX_ALIGNMENT sizeof(unsigned long) /* platform word */
private:
ngx_pool_s* pool;//指向ngx内存池的入口指针
//小块内存分配
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);
};
ngx_mem_pool.cpp
#include "ngx_mem_pool.h"
void* ngx_mem_pool::ngx_create_pool(size_t size)
{
ngx_pool_s* p;
p =(ngx_pool_s*) malloc( size);
if (p == nullptr) {
return nullptr;
}
p->d.last = (u_char*)p + sizeof(ngx_pool_s);
p->d.end = (u_char*)p + size;
p->d.next = nullptr;
p->d.failed = 0;
size = size - sizeof(ngx_pool_s);
p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;
p->current = p;
p->large = nullptr;
p->cleanup = nullptr;
pool = p;
return pool;
}
//考虑内存池字节对齐,从内存池申请size大小的内存
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_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_block( size_t size)
{
u_char* m;
size_t psize;
ngx_pool_s* p, * newpool;
psize = (size_t)(pool->d.end - (u_char*)pool);
m =(u_char*) malloc(psize);
if (m == NULL) {
return NULL;
}
newpool = (ngx_pool_s*)m;
newpool->d.end = m + psize;
newpool->d.next = NULL;
newpool->d.failed = 0;
m += sizeof(ngx_pool_data_t);
m = ngx_align_ptr(m, NGX_ALIGNMENT);
newpool->d.last = m + size;
for (p = pool->current; p->d.next; p = p->d.next) {
if (p->d.failed++ > 4) {
pool->current = p->d.next;
}
}
p->d.next = newpool;
return m;
}
//大块内存池分配
void* ngx_mem_pool::ngx_palloc_large( size_t size)
{
void* p;
ngx_uint_t n;
ngx_pool_large_s* large;
p = malloc(size);
if (p == nullptr) {
return nullptr;
}
n = 0;
for (large = pool->large; large; large = large->next) {
if (large->alloc == nullptr) {
large->alloc = p;
return p;
}
if (n++ > 3) {
break;// 遍历4次后,若还没有找到被释放过的大块内存对应的信息
}
}
// 为了提高效率,直接在小块内存中申请空间保存大块内存的信息
large = (ngx_pool_large_s*)ngx_palloc_small( sizeof(ngx_pool_large_s), 1);
if (large == NULL) {
free(p);
return NULL;
}
large->alloc = p;
large->next = pool->large;
pool->large = large;
return p;
}
//释放大块内存
void ngx_mem_pool::ngx_pfree(void* p)
{
ngx_pool_large_s* l;
for (l = pool->large; l; l = l->next) {
if (p == l->alloc) {
free(l->alloc);
l->alloc = nullptr;
return ;
}
}
}
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) {
ngx_memzero(p,size);
}
return p;
}
//内存池销毁
void ngx_mem_pool::ngx_destroy_pool()
{
ngx_pool_s* p, * n;
ngx_pool_large_s* l;
ngx_pool_cleanup_s* c;
for (c = pool->cleanup; c; c = c->next) {
if (c->handler) {
c->handler(c->data);
}
}
for (l = pool->large; l; l = l->next) {
if (l->alloc) {
//ngx_free(l->alloc);
free(l->alloc);
}
}
for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
//ngx_free(p);
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; l = l->next) {
if (l->alloc) {
//ngx_free(l->alloc);
free(l->alloc);
}
}
/*原写法
for (p = pool; p; 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 = 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->chain = nullptr;
pool->large = nullptr;
}
//回调清理
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;
}
testnginxpool.cpp
#include <stdio.h>
#include "ngx_mem_pool.h"
typedef struct Data stData;
struct Data
{
char* ptr;
FILE* pfile;
};
void func1(void* p1)//void (*)(void*)
{
char* p = (char*)p1;
printf("free ptr mem!");
free(p);
}
void func2(void* pf1)
{
FILE* pf = (FILE*)pf1;
printf("close file!");
fclose(pf);
}
int main()
{
ngx_mem_pool mempool;
// 512 - sizeof(ngx_pool_t) - 4095 => max
if (mempool.ngx_create_pool(512) == nullptr)
{
printf("ngx_create_pool fail...");
return -1;
}
void* p1 = mempool.ngx_palloc( 128); // 从小块内存池分配的
if (p1 == nullptr)
{
printf("ngx_palloc 128 bytes fail...");
return -1;
}
stData* p2 = (stData*)mempool.ngx_palloc(512); // 从大块内存池分配的
if (p2 == nullptr)
{
printf("ngx_palloc 512 bytes fail...");
return -1;
}
p2->ptr = (char *)malloc(12);
strcpy(p2->ptr, "hello world");
p2->pfile = fopen("data.txt", "w");
ngx_pool_cleanup_s* c1 = mempool.ngx_pool_cleanup_add(sizeof(char*));
c1->handler = func1;
c1->data = p2->ptr;
ngx_pool_cleanup_s* c2 = mempool.ngx_pool_cleanup_add(sizeof(FILE*));
c2->handler = func2;
c2->data = p2->pfile;
mempool.ngx_destroy_pool(); // 1.调用所有的预置的清理函数 2.释放大块内存 3.释放小块内存池所有内存
return 0;
}
运行结果:
移植成功