nginx内存池的代码实现__2019.04.13

高效的内存管理程序。

高并发的情况下(io密集型项目),实现内存的高效管理。

1.语言中的malloc是系统调用。关于系统调用的详解《程序员的自我修养》第12章。

malloc是靠边界标识法来管理的。注意:malloc返回的地址是头下面的地址。此边界包括:该段空间的大小、是否占用。

2.关于这部分的内容可以详见 严魏敏《数据结构与算法》

3.用大的内存只能从堆上申请。

4.内存池的概念。

5.1024byte=1kB、1024kB=1MB、1024MB=1GB。

服务器程序是7*24小时运行的,内存泄漏会造成服务器宕机。

高并发的情况下,传统的内存管理方式的弊端。

nginx为什么比传统方式高效,优势?

高并发c/c++传统内存操作的弊端。

弊端如何解决?

内存池技术。

Nginx内存池实现。

1.高并发c/c++传统内存操作的弊端:

标准c库内存操作函数在高并发时有什么弊端?

malloc、calloc、realloc、free

高并发较小内存块的申请,造成频繁的系统调用,降低系统的执行效率。

频繁的申请会造成系统内存碎片,降低内存使用效率。

为了适应不同的处理器架构,内存对齐的方式是不一样的。

容易造成内存泄漏,造成资源枯竭。

使用系统API,申请资源也必须使用,否则也会造成内存泄漏。

代码是一种逻辑性很强的东西。

内存分配与释放的逻辑在程序中相隔较远时,降低程序的稳定性。

snprintf();//格式化输出到

分配的内存如果不使用的话,就不会分配实际的物理内存(延迟分配)

Linux有用户态和内核态的区别。

JVM的垃圾回收机制。

弊端如何解决?

内存池怎样处理内存碎片。

设计内存池时,逻辑应该简单。降低不同模块间的耦合度。

高并发时内存池的生命周期很短。所以不会产生过多的内存碎片。即使有内存碎片,生命周期很短,内存碎片可以很快被处理掉,防止碎片的堆积和内存的泄漏。

内存池的生存周期应该与链接具有相同的周期。

Nginx内存池的实现:

对每个链接都会单独建立内存池,不单独释放,链接结束统一释放。

Nginx对于大内存块单独使用链表中。

代码很讲究效率和质量。

内存数据块:用来表示该内存块的状态。表示当时该内存块的事情情况。

内存池由多个内存块连起来的。

typedef struct {
     u_char               *last;
     u_char               *end;
     ngx_pool_t           *next;
     ngx_uint_t            failed;
 } ngx_pool_data_t;

struct ngx_pool_s {
     ngx_pool_data_t       d;
     size_t                max;
     ngx_pool_t           *current;
     ngx_chain_t          *chain;
     ngx_pool_large_t     *large;
     ngx_pool_cleanup_t   *cleanup;
     ngx_log_t            *log;
};
大内存块的申请是直接从操作系统中申请,并把它连在内存池中统一管理。

失败次数达到5次,就认为该内存块不能使用。

系统中出现的任何错误都应该写入系统日志:系统出错日志、系统完整的关键运行日志。

nginx.h

#ifndef NGINX_H
#define NGINX_H
typedef unsigned char u_char;
typedef struct ngx_pool_large_s  ngx_pool_large_t;
typedef struct ngx_pool_s ngx_pool_t;

struct ngx_pool_large_s {
	ngx_pool_large_t     *next;
	void                 *alloc;
};

typedef struct {
	u_char               *last;
	u_char               *end;
	ngx_pool_t           *next;
	size_t            failed;
} ngx_pool_data_t;

typedef struct ngx_pool_cleanup_s  ngx_pool_cleanup_t;

struct ngx_pool_s {
	ngx_pool_data_t       d;//数据区域
	size_t                max;//内存池的大小
	ngx_pool_t           *current;//当前的内存池块
	ngx_pool_large_t     *large;//大块的数据	
};

class NgxMemPool
{
public:
	//创建ngx的内存池
	ngx_pool_t* ngx_create_pool(size_t size);
	//销毁ngx内存池
	void ngx_destroy_pool();
	//重置内存池
	void ngx_reset_pool();
	//开辟内存,对齐
	void *ngx_palloc(size_t size);
	//开辟内存,不对齐
	void *ngx_pnalloc(size_t size);
	//如果小于max,按小块内存分配
	void *ngx_palloc_small(size_t size,size_t align);
	//如果大于max,按大块内存分配
	void *ngx_palloc_large(size_t size);
	//开辟新的内存块
	void *ngx_palloc_block(size_t size);
	//把内存归还给内存池
	void ngx_pfree(void *p);
private:
	ngx_pool_t *pool;
};
#endif
#include "Nginx.h"
#include <iostream>
using namespace std;

#define NGX_ALIGNMENT   sizeof(unsigned long) 
#define  NGX_MAX_ALLOC_FROM_POOL 1024//和操作系统内存页大小有关
//计算内存对齐的大小
#define ngx_align_ptr(p, a)  (u_char *)(((uintptr_t)(p)+((uintptr_t)a - 1)) & ~((uintptr_t)a - 1))

//首先只是创建一个块,并进行初始化
ngx_pool_t *  NgxMemPool::ngx_create_pool(size_t size)//创建ngx的内存池
{
	size = size + sizeof(ngx_pool_t);
	ngx_pool_t *p = (ngx_pool_t *)malloc(size );
	/*//初始状态:last指向ngx_pool_t结构体之后数据取起始位*/
	p->d.last = (u_char *)p + sizeof(ngx_pool_t);//p指数据结构占用的大小
	/*end指向分配的整个size大小的内存的末尾*/
	p->d.end = (u_char *)p + size;
	p->d.next = NULL;
	p->d.failed = 0;
	//size的大小等于申请的size减去数据部分
	size = size - sizeof(ngx_pool_t);
	p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;
	p->large = NULL;
	p->current = p;//创建第一个块时指向自己,failed<5//表示自己还能分配
	pool = p;
	return p;
}

//重置内存池
void NgxMemPool::ngx_reset_pool()
{
	ngx_pool_t *p;
	ngx_pool_large_t *k;
	/*释放大的内存块*/
	for (k = pool->large;k; k = k->next)
	{
		if ( k->alloc)
		{
			free(k->alloc);
			k->alloc = NULL;
		}
	}
	pool->large = NULL;
	/*重置小的内存块*/
	for (p = pool; p; p = p->d.next)
	{
		p->d.last = (u_char *)p + sizeof(ngx_pool_t);
		p->d.failed = 0;
	}
	pool->current = pool;
	
	pool->large = NULL;
}

/*
怎样从内存池中申请内存
*/

//开辟内存,对齐
void *NgxMemPool::ngx_palloc(size_t size)//按指定大小对齐,分配对齐好的内存。
{
	if (size < pool->max)
	{
		return ngx_palloc_small( size, 1);//1表示申请的内存按几字节对齐
	}
	else
		return ngx_palloc_large(size);//大的内存块是不走池子的。
}
//开辟内存,不对齐
void *NgxMemPool::ngx_pnalloc(size_t size)
{
	if (size < pool->max)
	{
		return ngx_palloc_small(size, 0);
	}
	else
		return ngx_palloc_large(size);
}
void *NgxMemPool::ngx_palloc_small(size_t size, size_t align)//从池子中找小内存块
{
	u_char *m;
	ngx_pool_t *p=pool;
	
	do{
		m = p->d.last;
		if (align)//如果定义了按多少对小内存块进行对齐
		{
			m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);
		}
		if ((size_t)(p->d.end - m) >= size)
		{//如果在当前内存块有效范围内,进行内存指针的移动
			p->d.last = m + size;//last向下移动
			return m;
		}
		p = p->d.next;
	} while (p);//如果有下一块,就跳到下一块
	return ngx_palloc_block(size);//如果下一个内存块没有的话,就申请一个内存块
}
//开辟内存,不对齐
void *NgxMemPool::ngx_palloc_block(size_t size)//////开辟下一个next
{
	u_char *m;
	size_t  psize;
	ngx_pool_t *p, *new_chunk;
	//计算内存池第一个内存块的大小
	psize = (size_t)(pool->d.end - (u_char*)pool);//pool是内存池的句柄,永远对应的都是第一个内存块

	m = (u_char*)malloc(psize);//这时候又分配4096的内存块
	if (m == NULL)
	{
		return NULL;
	}
	new_chunk = (ngx_pool_t *)m;
	/*设置新的内存块*///初始化内存块
	new_chunk->d.end = m + psize;//指向内存可以分配的地方
	new_chunk->d.next = NULL;
	new_chunk->d.failed = 0;
	/*将指针一移到数据段的后面的位置,作为起始位置*/
	m = m + sizeof(ngx_pool_data_t);
	m = ngx_align_ptr(m, NGX_ALIGNMENT);
	new_chunk->d.last = m + size;

	//current = pool->current;
	//当申请一个新的块的时候,就说明前面的内存分配都失败了。
	//当failed大于5时,就让current指向下一个内存块
	for (p = pool->current; p->d.next; p = p->d.next)
	{
		if (p->d.failed++ > 4)
		{
			pool->current = p->d.next;
		}
	}
	p->d.next = new_chunk;//把新的内存块挂在链表上

	return m;
}

//一个好的设计,越简单越好,越实用越好
//做软件工程师是很辛苦的
//做IT加班是常见的
//要不断的提升自己,不要让自己原地踏步
void *NgxMemPool::ngx_palloc_large(size_t size)
{
	void *p;
	size_t n;
	ngx_pool_large_t *large;
	
	// 直接在系统堆中分配一块空间
	p = malloc(size);
	if (NULL == p)
		return NULL;
	n = 0;

	for (large = pool->large; large; large = large->next)//for循环去遍历alloc,把这段内存块挂上去
	{
		if (large->alloc == NULL)
		{
			large->alloc = p;
			return p;
		}
		if (n++ > 3)
		{
			break;
		}
	}
	
	//如果在三次内没有找到空的large结构体,则创建一个
	large = (ngx_pool_large_t *)ngx_palloc_small(sizeof(ngx_pool_large_t),1);
	if (large == NULL)
	{
		free(p);//创建失败的话,释放开辟的空间
		return NULL;
	}
	large->alloc = p;
	large->next = pool->large;
	pool->large = large;
	return p;

}

//销毁ngx内存池
void NgxMemPool::ngx_destroy_pool()
{
	ngx_pool_t *p, *n;
	ngx_pool_large_t *k;
	//首先遍历大的内存块,进行释放
	for (k = pool->large; k; k = k->next)
	{
		if (k->alloc)
		{
			free(k->alloc);
		}
	}
	//遍历池子中的内存块,有几个内存块,就释放几次
	for (p = pool; n = pool->d.next; p = n, n = n->d.next)
	{
		free(p);
		if (n == NULL)
			break;
	}
}

//把内存归还给内存池
void NgxMemPool::ngx_pfree(void *p)
{
	ngx_pool_large_t *k;
	for (k = pool->large;; k = k->next)
	{
		if (p == k->alloc)
		{
			free(k->alloc);
			k->alloc = NULL;
		}
	}
}

//十个想法,不如一个行动

int main()
{
	NgxMemPool pool;
	pool.ngx_create_pool(500);
	int *p=(int *)pool.ngx_pnalloc(50);

	for (int i = 0; i < 50; ++i)
	{
		p[i] = i;
	}
	for (int i = 0; i < 50; ++i)
	{
		cout << p[i] << endl;
	}
	pool.ngx_pfree(p);
	pool.ngx_destroy_pool();
	return 0;
}
 
 
 

猜你喜欢

转载自blog.csdn.net/weixin_40316053/article/details/89276576