nginx 源码学习笔记(一)

nginx 源码学习笔记(一)

最近不是很忙,所以闲暇之余学习开源代码,nginx是一个很好的选择,是一个教科书般的存在,但是本人是小菜鸟一个,linux下的开发…嗯嗯,正在学习中,所以我的学习环境是vs2010,见谅见谅啊。

源码地址:
https://download.csdn.net/download/qq_37825248/10786030

首先找到main函数,在core/nginx.c路径下
在这里插入图片描述
现在可以开始nginx之旅了…

内存池的必要

——通常我们用new和malloc来分配内存的话,由于申请的大小不确定,所以频繁的使用时会带来内存碎片和效率降低的问题。为了克服这种问题提出了内存池的概念。内存池是一种内存分配方式。内存池的优点是可以有效的减少内存碎片化,分配内存更加快速,减少内存泄漏等优点。
——因此,nginx为了长远的运行考虑,也是通过这种方式,对内存进行统一申请,释放,大大提高了运行效率。先来看看nginx是如何管理的。

  1. 基本的内存池结构
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;
  1. 内存池的创建

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;
}
  1. 内存申请

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;
}
  1. 内存池的重置

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;
}
  1. 内存池的清理

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;
}

内存清理只释放大块的内存,对于小内存不清理

  1. 内存池的销毁

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值