nginx 源码学习笔记(一)
最近不是很忙,所以闲暇之余学习开源代码,nginx是一个很好的选择,是一个教科书般的存在,但是本人是小菜鸟一个,linux下的开发…嗯嗯,正在学习中,所以我的学习环境是vs2010,见谅见谅啊。
源码地址:
https://download.csdn.net/download/qq_37825248/10786030
首先找到main函数,在core/nginx.c路径下
现在可以开始nginx之旅了…
内存池的必要
——通常我们用new和malloc来分配内存的话,由于申请的大小不确定,所以频繁的使用时会带来内存碎片和效率降低的问题。为了克服这种问题提出了内存池的概念。内存池是一种内存分配方式。内存池的优点是可以有效的减少内存碎片化,分配内存更加快速,减少内存泄漏等优点。
——因此,nginx为了长远的运行考虑,也是通过这种方式,对内存进行统一申请,释放,大大提高了运行效率。先来看看nginx是如何管理的。
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; //日志信息
};
typedef struct {
u_char *last; //内存已使用位置
u_char *end; //内存结束位置
ngx_pool_t *next; //下个内存池块
ngx_uint_t failed; //申请内存的失败的次数,如果失败4次,则另外申请内存块
} ngx_pool_data_t;
ngx_create_pool(size_t size, ngx_log_t *log)
ngx_create_pool(size_t size, ngx_log_t *log)
{
ngx_pool_t *p;
p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);//内存对齐
if (p == NULL) {
return NULL;
}
p->d.last = (u_char *) p + sizeof(ngx_pool_t); //除去结构体本身需要的内存
p->d.end = (u_char *) p + size; //把指针移动到末尾
p->d.next = NULL;
p->d.failed = 0;
size = size - sizeof(ngx_pool_t);
p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;
p->current = p;
p->chain = NULL;
p->large = NULL;
p->cleanup = NULL;
p->log = log;
return p;
}
ngx_palloc 从内存池中申请内存,并内存对其
void *ngx_palloc(ngx_pool_t *pool, size_t size)
{
u_char *m;
ngx_pool_t *p;
if (size <= pool->max) {//如果申请的大小没有超过内存池的最大值
p = pool->current;
do {
m = ngx_align_ptr(p->d.last, 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(pool, size);
//如果当前内存不够且没有可用的内存块,就从新申请内存,链接到内存数据链表
}
return ngx_palloc_large(pool, size); //申请更大的内存块
}
对于内存对齐,现在来看看内存对齐的宏定义
#define ngx_align_ptr(p, a) \
(u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))
当初我看到这里的时候,对于内存对齐很不理解,为什么要内存对齐呢·······而且这个公式看起来好复杂啊,所以在这里简单的提一句,想要详细了解还是需要后期好好理解,打个比方,这里有1-16个箱子,每个箱子能够存放4个球,如果存放红色的6个球需要两个箱子,1号箱子放满,但是2号箱子却没有放满,如果再放白色的12个球,把没有放满的箱子放满,那么还需要三个箱子,3号放满,4号放满,5号没有满,然后再继续存放球····乍一看,哇塞,内存好节省哦,但是如果你要去找白色的球的时候呢,需要在从2号开始找,3号,4号,5号,打开4次才能找到,如果一开始从3号箱子开始存放,是不是3次就能找到了呢,降低了访问的次数,提高了效率,所以内存对齐就是这个作用,将指针移动到需要内存对齐下一块内存的首地址上,开始存放数据,虽然会对内存造成浪费,但是能够有效的提高访问效率。
但是上面的宏定义如果做到的呢,输入的a为需要对齐的大小,一般为2的幂次方,如果输入首地址为7,对齐大小为sizeof(*void),,减1取反后的二进制为0100,输入的首地址加上减1后的对齐大小为10,二进制为1010,&运算后值为1000,十进制为8,是不是4的倍数呢,变成了对齐后的首地址了。
ngx_pnalloc从内存池中申请内存
void *ngx_pnalloc(ngx_pool_t *pool, size_t size)
{
u_char *m;
ngx_pool_t *p;
if (size <= pool->max) {
p = pool->current;
do {
m = p->d.last; //当前可用内存首地址
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(pool, size);
}
return ngx_palloc_large(pool, size);
}
这个和ngx_palloc比较就能看出来,申请内存没有经过内存对齐。
再看看内存池不够的时候,内存块申请
static void *ngx_palloc_block(ngx_pool_t *pool, size_t size)
{
u_char *m;
size_t psize;
ngx_pool_t *p, *new;
psize = (size_t) (pool->d.end - (u_char *) pool);
m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);
if (m == NULL) {
return NULL;
}
//新的内存节点
new = (ngx_pool_t *) m;
new->d.end = m + psize;
new->d.next = NULL;
new->d.failed = 0;
m += sizeof(ngx_pool_data_t);
m = ngx_align_ptr(m, NGX_ALIGNMENT);
new->d.last = m + size;
for (p = pool->current; p->d.next; p = p->d.next) {
//当内存申请失败4次之后
if (p->d.failed++ > 4) {
pool->current = p->d.next;
}
}
//原先的内存指向下一个节点
p->d.next = new;
return m;
}
ngx_reset_pool(ngx_pool_t *pool)内存池初始化
void ngx_reset_pool(ngx_pool_t *pool)
{
ngx_pool_t *p;
ngx_pool_large_t *l;
//把大的内存池释放
for (l = pool->large; l; l = l->next) {
if (l->alloc) {
ngx_free(l->alloc);
}
}
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->chain = NULL;
pool->large = NULL;
}
ngx_pfree(ngx_pool_t *pool, void *p)
ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p)
{
ngx_pool_large_t *l;
//只检查是否是大内存块,如果是大内存块则释放
for (l = pool->large; l; l = l->next) {
if (p == l->alloc) {
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
"free: %p", l->alloc);
ngx_free(l->alloc);
l->alloc = NULL;
return NGX_OK;
}
}
return NGX_DECLINED;
}
内存清理只释放大块的内存,对于小内存不清理
ngx_destroy_pool(ngx_pool_t *pool)
void ngx_destroy_pool(ngx_pool_t *pool)
{
ngx_pool_t *p, *n;
ngx_pool_large_t *l;
ngx_pool_cleanup_t *c;
//查看清理结构体,并调用清理函数
for (c = pool->cleanup; c; c = c->next) {
if (c->handler) {
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
"run cleanup: %p", c);
c->handler(c->data);
}
}
//释放大块内存
for (l = pool->large; l; l = l->next) {
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc);
if (l->alloc) {
ngx_free(l->alloc);
}
}
#if (NGX_DEBUG)
/*
* we could allocate the pool->log from this pool
* so we cannot use this log while free()ing the pool
*/
for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
"free: %p, unused: %uz", p, p->d.end - p->d.last);
if (n == NULL) {
break;
}
}
#endif
//释放小块内存
for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
ngx_free(p);
if (n == NULL) {
break;
}
}
}
关于内存池的图,推荐如下:
https://blog.csdn.net/liujiyong7/article/details/17794663