nginx 源码学习笔记(七)——内存分配相关源码分析

内存分配相关

1.      系统功能封装

内存相关的操作主要在os/unix/ngx_alloc.{h,c} 和 core/ngx_palloc.{h,c}中。

其中os/unix/ngx_alloc.{h,c}封装了最基本的内存分配函数,是对c原有的malloc/free/memalign等函数的封装,对应函数为:

a.ngx_alloc:对malloc进行了简单的封装;

  1. void *  
  2. ngx_alloc(size_t size, ngx_log_t *log)  
  3. {  
  4.     void  *p;  
  5.     p = malloc(size);  
  6.     if (p == NULL) {  
  7.         ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,  
  8.                       "malloc(%uz) failed", size);    }  
  9.     ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, "malloc: %p:%uz", p, size);  
  10.     return p;  
  11. }  

b.ngx_calloc:使用ngx_alloc分配内存,并且把内存赋值0:

  1. void *  
  2. ngx_calloc(size_t size, ngx_log_t *log)  
  3. {  
  4.     void  *p;  
  5.     p = ngx_alloc(size, log);  
  6.     if (p) {  
  7.         ngx_memzero(p, size);    }在core/ngx_string.h中定义  
  8. // #define ngx_memzero(buf, n)       (void) memset(buf, 0, n) 初始化为0  
  9.     return p;  
  10. }  

c. ngx_memalign  返回基于一个指定的alignment大小的数值为对齐基数的空间

d.ngx_free    内存释放操作

2.      nginx内存池

为了方便系统模块对内存的使用,方便内存的管理,nginx自己是信了进程池机制来进行内存的分配和释放,首先nginx会在特定的生命周期帮你统一建立内存池,当需要进行内存分配的时候同一通过内存池中的内存进行分配,最后nginx会在适当的时候释放内存吃的资源,开发者只要在需要的时候对内存进行申请即可,不用过多考虑释放的问题,这也就是在os/unix/ngx_alloc.c文件中没有看到free操作的原因吧。

 

下面来看一下内存池的主要结构:

  1. <span style="font-size:16px;">ngx_palloc.h  
  2. struct ngx_pool_s {  
  3.     ngx_pool_data_t       d;  
  4.     size_t                max;  
  5.     ngx_pool_t           *current;  
  6.     ngx_chain_t          *chain;  
  7.     ngx_pool_large_t     *large;  
  8.     ngx_pool_cleanup_t   *cleanup;  
  9.     ngx_log_t            *log;  
  10. };  
  11. typedef struct {  
  12.     u_char               *last;  
  13.     u_char               *end;  
  14.     ngx_pool_t           *next;  
  15.     ngx_uint_t            failed;  
  16. } ngx_pool_data_t;  
  17.   
  18. ngx_core.h  
  19. typedef struct ngx_pool_s        ngx_pool_t;  
  20. typedef struct ngx_chain_s       ngx_chain_t;  
  21. </span>  

下面是几个比较重要的操作


  1. <span style="font-size:16px;">src/core/ngx_palloc.c  
  2. //创建内存池  
  3. ngx_pool_t *  
  4. ngx_create_pool(size_t size, ngx_log_t *log)  
  5. {  
  6.     ngx_pool_t  *p;  
  7.   
  8.     p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);  //创建对其空间  
  9.     if (p == NULL) {  
  10.         return NULL;  
  11.     }  
  12.   
  13.     p->d.last = (u_char *) p + sizeof(ngx_pool_t);    //初始指向ngx_pool_t结构体后面  
  14.     p->d.end = (u_char *) p + size;                   //整个结构体的结尾  
  15.     p->d.next = NULL;                                 //没有next  
  16.     p->d.failed = 0;  
  17.   
  18.     size = size - sizeof(ngx_pool_t);    //剩余大小  
  19. p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;//最大不超过NGX_MAX_ALLOC_FROM_POOL  
  20. //#define NGX_MAX_ALLOC_FROM_POOL  (ngx_pagesize - 1)  
  21.   
  22.     p->current = p;  
  23.     p->chain = NULL;  
  24.     p->large = NULL;  
  25.     p->cleanup = NULL;  
  26.     p->log = log;  
  27.   
  28.     return p;  
  29. }  
  30.   
  31. //销毁内存池  
  32. void  
  33. ngx_destroy_pool(ngx_pool_t *pool)  
  34. {  
  35.     ngx_pool_t          *p, *n;  
  36.     ngx_pool_large_t    *l;  
  37.     ngx_pool_cleanup_t  *c;  
  38.   
  39.     for (c = pool->cleanup; c; c = c->next) {//如果注册了clenup(一种链表结构),会依次调用clenup的handler进行清理  
  40.         if (c->handler) {  
  41.             ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,  
  42.                            "run cleanup: %p", c);  
  43.             c->handler(c->data);  
  44.         }  
  45.     }  
  46.   
  47.     for (l = pool->large; l; l = l->next) { //遍历链表,释放所有large内存  
  48.   
  49.         ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc);  
  50.   
  51.         if (l->alloc) {  
  52.             ngx_free(l->alloc);  
  53.         }  
  54.     }  
  55.   
  56. #if (NGX_DEBUG)  //等译debug级别,如果为true,会打印日志  
  57.   
  58.     /* 
  59.      * we could allocate the pool->log from this pool 
  60.      * so we cannot use this log while free()ing the pool 
  61.      */  
  62.   
  63.     for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {  
  64.         ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0,  
  65.                        "free: %p, unused: %uz", p, p->d.end - p->d.last);  
  66.   
  67.         if (n == NULL) {  
  68.             break;  
  69.         }  
  70.     }  
  71.   
  72. #endif  
  73.   
  74.     for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {//遍历链表 ,释放内存空间  
  75.         ngx_free(p);  
  76.   
  77.         if (n == NULL) {  
  78.             break;  
  79.         }  
  80.     }  
  81. }  
  82.   
  83. //重置内存池  
  84. void  
  85. ngx_reset_pool(ngx_pool_t *pool)  
  86. {  
  87.     ngx_pool_t        *p;  
  88.     ngx_pool_large_t  *l;  
  89.   
  90.     for (l = pool->large; l; l = l->next) { //释放掉所有large段内存  
  91.         if (l->alloc) {  
  92.             ngx_free(l->alloc);  
  93.         }  
  94.     }  
  95.   
  96.     pool->large = NULL;  
  97.   
  98.     for (p = pool; p; p = p->d.next) {  
  99.         p->d.last = (u_char *) p + sizeof(ngx_pool_t);将指针重新指向ngx_pool_t(和创建时一样)  
  100.     }  
  101. }  
  102.   
  103. //从内存池里分配内存  
  104. void * ngx_palloc(ngx_pool_t *pool, size_t size)  
  105. void * ngx_pnalloc(ngx_pool_t *pool, size_t size)  
  106. void * ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment)  
  107. void * ngx_pcalloc(ngx_pool_t *pool, size_t size)  
  108.   
  109. 这里以ngx_palloc为例讲解,其他大同小异:  
  110. void *  
  111. ngx_palloc(ngx_pool_t *pool, size_t size)  
  112. {  
  113.     u_char      *m;  
  114.     ngx_pool_t  *p;  
  115.     if (size <= pool->max) { //判断分配内存是否大于pool->max,如果小于等于  
  116.         p = pool->current; //尝试从链表的current开始遍历,  
  117.         do {  
  118.             m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);  
  119. //#define ngx_align_ptr(p,a)    
  120. //(u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))  
  121.   
  122.             if ((size_t) (p->d.end - m) >= size) {   //当找到可以分配的空间时  
  123.                 p->d.last = m + size;  
  124.                 return m;                   //分配内存后返回  
  125.             }  
  126.             p = p->d.next;  
  127.         } while (p);  
  128.         return ngx_palloc_block(pool, size);//如果无法分配内存,就生成一个新的节点,同时pool->current指针指向新的位置  
  129.     }  
  130.     return ngx_palloc_large(pool, size);  //如果分配的内存大于pool->max则在large链表分配一段内存  
  131. }  
  132.   
  133. //释放指定的内存  
  134. ngx_int_t  
  135. ngx_pfree(ngx_pool_t *pool, void *p){  
  136.     ngx_pool_large_t  *l;  
  137.     for (l = pool->large; l; l = l->next) {  
  138.         if (p == l->alloc) {        //存在alloc注册  
  139.             ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,  
  140.                            "free: %p", l->alloc);  
  141.             ngx_free(l->alloc);  
  142.             l->alloc = NULL;  
  143.             return NGX_OK;  
  144.         }  
  145.     }  
  146.     return NGX_DECLINED;  
  147. }//由代码可以看出,这个操作只有在内存large链表里面注册内存才会真正释放,如果分配的是普通的内存,则会在destory_pool的时候统一释放。  
  148.   
  149. //注册cleanup回调函数  
  150. ngx_pool_cleanup_t *  
  151. ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)  
  152. {  
  153.     ngx_pool_cleanup_t  *c;  
  154.     c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t));  //分配cleanup空间  
  155.     if (c == NULL) {  
  156.         return NULL;  
  157.     }  
  158.     if (size) {  
  159.         c->data = ngx_palloc(p, size);         //为cleanup结构体分配data空间  
  160.         if (c->data == NULL) {  
  161.             return NULL;  
  162.         }  
  163.     } else {  
  164.         c->data = NULL;  
  165.     }  
  166.     c->handler = NULL;  
  167.     c->next = p->cleanup;  
  168.     p->cleanup = c;               // 增加cleanup  
  169.     ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "add cleanup: %p", c);  
  170.     return c;                    //返回结构体分配的空间  
  171. }</span>  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值