nginx的内存管理

先来看内存池的实现,nginx的内存池实现的非常简单。

这里内存池的一些图表可以看老朱同学的slides :

http://blog.zhuzhaoyuan.com/2009/09/nginx-internals-slides-video/

当内存池初始化的时候(下面会分析到)ngx_poll_s只相当于内存池的一个头,保存了当前内存池的一些必要信息而已。

当从内存池存取数据的时候,nginx是分为两种类型来处理得,一种是小块数据,它是直接从内存池中取得数据,另一方面,当为大块数据时,它是直接malloc一块数据(也就是从内存池外部分配数据),然后保存这个指针到内存池。可以看到很多内存池,比如py的内存池实现,也基本是这个思想。这里的细节,我们将会在下面分析内存池相关函数的时候详细分析。

这里还有一个要注意就是这些子内存池和父内存池是不一样的,我们后面分析函数的时候会详细介绍。

大小块数据得分割线是你创建内存池时传递进来的size和页大小之间的最小值。

下面就是内存池的结构:

Java代码  

  1. struct ngx_pool_s {
  2. ///数据区的指针
  3. ngx_pool_data_t       d;
  4. ///其实也就是内存池所能容纳的最大值。
  5. size_t                max;
  6. ///指向当前的内存池的头。
  7. ngx_pool_t           *current;
  8. ///这个主要是为了讲所有的内存池都链接起来。(他会创建多个内存池的)
  9. ngx_chain_t          *chain;
  10. ///这个链表表示大的数据块
  11. ngx_pool_large_t     *large;
  12. ///这个就是清理函数链表
  13. ngx_pool_cleanup_t   *cleanup;
  14. ngx_log_t            *log;
  15. };

然后我们一个个来看上面的链表。首先是数据区的指针ngx_pool_data_t。

这个结构很简单,它就是包含了我们所需要操作这个内存池的数据的一些指针。

其中last表示当前的数据区的已经使用的数据的结尾。

end表示当前的内存池的结尾。也就是说end-last就是内存池未使用的大小。

我们要知道当我们从一个内存池请求一块内存时,如果此时内存池已经满掉,这是一般都是扩大内存池,而nginx中不是这么做的,它会直接再分配一个内存池,然后链接到data的next指针上。也就是说在nginx中,其实每个内存池都会包含一些子内存池。因此我们请求内存的时候都会遍历这些子内存池。

failed域主要是为了标记我们请求内存(小块内存)由于内存池空间不够,我们需要重新分配一个子内存池的次数。 下面分析函数的时候会再会看到这个域。

Java代码  

  1. typedef struct {
  2. u_char               *last;
  3. u_char               *end;
  4. //指向下一块内存池
  5. ngx_pool_t           *next;
  6. ///失败标记
  7. ngx_uint_t            failed;
  8. } ngx_pool_data_t;

ngx_chain_t这里就先不介绍了,我们现在只需要知道它是与buf相关的。

然后是ngx_pool_large_s,它表示了大块的内存。可以看到这个结构非常简单,就是一个指针指向下一块large,一个alloc指向数据。

Java代码  

  1. struct ngx_pool_large_s {
  2. ngx_pool_large_t     *next;
  3. void                 *alloc;
  4. };

接下来是ngx_pool_cleanup_s,这个结构用来表示内存池中的数据的清理handler。

其中handler表示清理函数。 
data表示传递给清理函数的数据。 
next表示下一个清理handler,也就是说当destroy这个pool的时候会遍历清理handler链表,然后调用handler。

Java代码  

  1. struct ngx_pool_cleanup_s {
  2. ngx_pool_cleanup_pt   handler;
  3. void                 *data;
  4. ngx_pool_cleanup_t   *next;
  5. }

通过ngx_create_temp_buf创建一个buff,然后通过ngx_alloc_chain_link创建一个chain,然后通过cl->buf = rb->buf;将buff链接到chain中.

下面就是pool的内存图,摘自老朱同学的nginx internal。

ok,接下来我们来通过分析具体的函数,来更好的理解pool的实现。

首先来看ngx_create_pool也就是创建一个pool。

这里我们要知道虽然我们传递进来的大小是size可是我们真正能使用的数据区大小是要减去ngx_pool_t的大小的。

Java代码  

  1. ///内存池的数据区的最大容量。
  2. #define NGX_MAX_ALLOC_FROM_POOL  (ngx_pagesize - 1)
  3. ngx_pool_t *
  4. ngx_create_pool(size_t size, ngx_log_t *log)
  5. {
  6. ngx_pool_t  *p;
  7. ///可以看到直接分配size大小,也就是说我们只能使用size-sizeof(ngx_poll_t)大小
  8. p = ngx_alloc(size, log);
  9. if (p == NULL) {
  10. return NULL;
  11. }
  12. ///开始初始化数据区。
  13. ///由于一开始数据区为空,因此last指向数据区的开始。
  14. p->d.last = (u_char *) p + sizeof(ngx_pool_t);
  15. ///end也就是数据区的结束位置
  16. p->d.end = (u_char *) p + size;
  17. p->d.next = NULL;
  18. p->d.failed = 0;
  19. ///这里才是我们真正能使用的大小。
  20. size = size - sizeof(ngx_pool_t);
  21. ///然后设置max。内存池的最大值也就是size和最大容量之间的最小值。
  22. p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;
  23. ///current表示当前的内存池。
  24. p->current = p;
  25. ///其他的域置NULL。
  26. p->chain = NULL;
  27. p->large = NULL;
  28. p->cleanup = NULL;
  29. p->log = log;
  30. ///返回指针。
  31. return p;
  32. }

接下来我们来看如何从内存池中分配一块内存来使用。在nginx中有3个函数可以使用,分别是ngx_palloc,ngx_calloc,ngx_pnalloc。这三个函数的区别就是第一个函数分配的内存会对齐。第二个函数用来分配一块清0的内存,第三个函数分配的内存不会对齐。

由于这三个函数差不多,因此我们就只分析一个就够了。我们就来看ngx_palloc.

Java代码  

  1. void *
  2. ngx_palloc(ngx_pool_t *pool, size_t size)
  3. {
  4. u_char      *m;
  5. ngx_pool_t  *p;
  6. ///首先判断当前申请的大小是否超过max,如果超过则说明是大块,此时进入large
  7. if (size <= pool->max) {
  8. ///得到当前的内存池指针。
  9. p = pool->current;
  10. ///开始遍历内存池,
  11. do {
  12. ///首先对齐last指针。
  13. m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);
  14. ///然后得到当前内存池中的可用大小。如果大于请求大小,则直接返回当前的last,也就是数据的指针。
  15. if ((size_t) (p->d.end - m) >= size) {
  16. ///更新last,然后返回前面保存的last。
  17. p->d.last = m + size;
  18. return m;
  19. }
  20. ///否则继续遍历
  21. p = p->d.next;
  22. } while (p);
  23. ///到达这里说明内存池已经满掉,因此我们需要重新分配一个内存池然后链接到当前的data的next上。(紧接着我们会分析这个函数)
  24. return ngx_palloc_block(pool, size);
  25. }
  26. ///申请大块。
  27. return ngx_palloc_large(pool, size);
  28. }

接下来就来看ngx_palloc_block的实现,这个函数主要就是重新分配一块内存池,然后链接到当前内存池的数据区指针。

然后要注意这里重新new的这个内存池大小是和它的父内存池一样大的。

并且新得内存池只保存了ngx_pool_data_t这个结构,也就是说数据区直接跟在ngx_pool_data_t下面。

Java代码  

  1. static void *
  2. ngx_palloc_block(ngx_pool_t *pool, size_t size)
  3. {
  4. u_char      *m;
  5. size_t       psize;
  6. ngx_pool_t  *p, *new, *current;
  7. ///计算当前的内存池的大小。
  8. psize = (size_t) (pool->d.end - (u_char *) pool);
  9. ///再分配一个同样大小的内存池
  10. m = ngx_alloc(psize, pool->log);
  11. if (m == NULL) {
  12. return NULL;
  13. }
  14. new = (ngx_pool_t *) m;
  15. ///接下来和我们create一个内存池做的操作一样。就是更新一些指针
  16. new->d.end = m + psize;
  17. new->d.next = NULL;
  18. new->d.failed = 0;
  19. ///这里要注意了,可以看到last指针是指向ngx_pool_data_t的大小再加上要分配的size大小,也就是现在的内存池只包含了ngx_pool_data_t和数据。
  20. m += sizeof(ngx_pool_data_t);
  21. m = ngx_align_ptr(m, NGX_ALIGNMENT);
  22. new->d.last = m + size;
  23. ///设置current。
  24. current = pool->current;
  25. ///这里遍历所有的子内存池,这里主要是通过failed来标记重新分配子内存池的次数,然后找出最后一个大于4的,标记它的下一个子内存池为current。
  26. for (p = current; p->d.next; p = p->d.next) {
  27. if (p->d.failed++ > 4) {
  28. current = p->d.next;
  29. }
  30. }
  31. ///链接到最后一个内存池后面
  32. p->d.next = new;
  33. ///如果current为空,则current就为new。
  34. pool->current = current ? current : new;
  35. return m;
  36. }

这里解释一下为什么这样设置current,这里的主要原因是我们在ngx_palloc中分配内存是从current开始的,而这里也就是设置current为比较新分配的内存。而当failed大于4说明我们至少请求了4次内存分配,都不能满足我们的请求,此时我们就假设老的内存都已经没有空间了,因此我们就从比较新的内存块开始。

接下来是ngx_palloc_large,这个函数也是很简单就是malloc一块ngx_poll_large_t,然后链接到主的内存池上。

Java代码  

  1. static void *
  2. ngx_palloc_large(ngx_pool_t *pool, size_t size)
  3. {
  4. void              *p;
  5. ngx_uint_t         n;
  6. ngx_pool_large_t  *large;
  7. ///分配一块内存。
  8. p = ngx_alloc(size, pool->log);
  9. if (p == NULL) {
  10. return NULL;
  11. }
  12. n = 0;
  13. ///开始遍历large链表,如果有alloc(也就是内存区指针)为空,则直接指针赋值然后返回。一般第一次请求大块内存都会直接进入这里。并且大块内存是可以被我们手动释放的。
  14. for (large = pool->large; large; large = large->next) {
  15. if (large->alloc == NULL) {
  16. large->alloc = p;
  17. return p;
  18. }
  19. ///这里不太懂什么意思。
  20. if (n++ > 3) {
  21. break;
  22. }
  23. }
  24. ///malloc一块ngx_pool_large_t。
  25. large = ngx_palloc(pool, sizeof(ngx_pool_large_t));
  26. if (large == NULL) {
  27. ngx_free(p);
  28. return NULL;
  29. }
  30. ///然后链接数据区指针p到large。这里可以看到直接插入到large链表的头的。
  31. large->alloc = p;
  32. large->next = pool->large;
  33. pool->large = large;
  34. return p;
  35. }

ok,分配看完了,我们来看释放。这里要知道在nginx中,只有大块内存提供了free接口,可以供我们收工释放,而小块内存是没有提供这个接口的。也就是说小块内存只有当整个内存池被desrtoy掉时,才会被释放。

这里一些简单的函数就不分析了。 
比如ngx_pfree(ngx_pool_t *pool, void *p),这个函数就是从pool的large链表中找到p,然后free掉它。

ngx_pool_cleanup_t * 
ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)

这个函数也就是添加一个ngx_pool_cleanup_t到当前的pool上,然后返回,我们此时就能通过返回的结构来给对应的handler赋值。

而ngx_pool_cleanup_t这个主要是当内存池destroy的时候我们可能需要做一些清理工作,此时我们就能add这些清理handler到pool中,然后当内存池要释放的时候就会自动调用。

ok,现在来看pool 被free的实现。

这个函数主要是遍历large,遍历current,然后一一释放。

Java代码  

  1. void
  2. ngx_destroy_pool(ngx_pool_t *pool)
  3. {
  4. ngx_pool_t          *p, *n;
  5. ngx_pool_large_t    *l;
  6. ngx_pool_cleanup_t  *c;
  7. ///先做清理工作。
  8. for (c = pool->cleanup; c; c = c->next) {
  9. if (c->handler) {
  10. ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
  11. "run cleanup: %p", c);
  12. c->handler(c->data);
  13. }
  14. }
  15. ///free大块内存
  16. for (l = pool->large; l; l = l->next) {
  17. ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc);
  18. if (l->alloc) {
  19. ngx_free(l->alloc);
  20. }
  21. }
  22. ///遍历小块内存池。
  23. for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
  24. ///直接free掉。
  25. ngx_free(p);
  26. if (n == NULL) {
  27. break;
  28. }
  29. }
  30. }

通过上面我们可以看到在nginx中内存池中的小块数据是从来不释放的,这样就简化了内存池的操作。

接下来我们来看buf的实现。

buf分为两种类型,一种是file,一种是memory.因此这里会有文件的一些操作域。

可以看到buf相对于pool多了一个pos域(file_pos).这里我们要知道我们发送往套接字异或者其他的设备,我们这里会现将数据放到buf中,然后当设备或者套接字准备好了,我们就会从buf中读取,因此这里pos指针就是放到buf中的已经被执行的数据(也就是已经送往套接字)的位置。

Java代码  

  1. struct ngx_buf_s {
  2. ///pos表示已经执行的数据的位置。
  3. u_char          *pos;
  4. ///last和上面内存池中last一样,也就是使用的内存的最后一个字节的指针
  5. u_char          *last;
  6. ///文件指针
  7. off_t            file_pos;
  8. off_t            file_last;
  9. ///buf的开始指针
  10. u_char          *start;         /* start of buffer */
  11. u_char          *end;           /* end of buffer */
  12. ///这里表示这个buf从属于那个模块。
  13. ngx_buf_tag_t    tag;
  14. ngx_file_t      *file;
  15. ngx_buf_t       *shadow;
  16. ///一些标记
  17. /* the buf‘s content could be changed */
  18. unsigned         temporary:1;
  19. ///在内存中是不能改变的。
  20. unsigned         memory:1;
  21. ///是否是mmap的内存
  22. unsigned         mmap:1;
  23. unsigned         recycled:1;
  24. ///是否文件。
  25. unsigned         in_file:1;
  26. unsigned         flush:1;
  27. unsigned         sync:1;
  28. unsigned         last_buf:1;
  29. unsigned         last_in_chain:1;
  30. unsigned         last_shadow:1;
  31. unsigned         temp_file:1;
  32. /* STUB */ int   num;
  33. };

ok,接下来我们来看如何创建一个buf.在nginx中一般都是调用ngx_create_temp_buf来创建一个buf。函数很简单,就是从pool中分配内存然后初始化相关域。

Java代码  

  1. ngx_buf_t *
  2. ngx_create_temp_buf(ngx_pool_t *pool, size_t size)
  3. {
  4. ngx_buf_t *b;
  5. ///calloc一个buf,可以看到它调用的是calloc,也就是说都会清0.
  6. b = ngx_calloc_buf(pool);
  7. if (b == NULL) {
  8. return NULL;
  9. }
  10. ///然后从内存池中分配一块内存。并将这块内存链接到b->start.
  11. b->start = ngx_palloc(pool, size);
  12. if (b->start == NULL) {
  13. return NULL;
  14. }
  15. ///设置相关的域。
  16. b->pos = b->start;
  17. b->last = b->start;
  18. ///设置打消
  19. b->end = b->last + size;
  20. b->temporary = 1;
  21. return b;
  22. }

然后我们来看chain的实现,chain其实也就是多个buf组合而成的。它主要是用来缓存一些未发出去的,或者接收的buf 以及 writev以及readv而存在的。

ok我们来看chain的实现,其实它的实现很简单,就是一个单链表。

Java代码  

  1. struct ngx_chain_s {
  2. ///buf
  3. ngx_buf_t    *buf;
  4. ///下一个buf的指针。
  5. ngx_chain_t  *next;
  6. };

然后来看如何创建一个chain。这里取得一个chain后直接返回给供其他模块使用:

Java代码  

  1. ngx_chain_t *
  2. ngx_alloc_chain_link(ngx_pool_t *pool)
  3. {
  4. ngx_chain_t  *cl;
  5. ///取得pool的老的chain
  6. cl = pool->chain;
  7. ///如果chain已经存在,则直接返回这个chain,然后从pool的chain中删除这个chain。
  8. if (cl) {
  9. pool->chain = cl->next;
  10. return cl;
  11. }
  12. ///否则从内存池重新new一个chain。这里注意新建的这个chain是链接到pool的chain上的。
  13. cl = ngx_palloc(pool, sizeof(ngx_chain_t));
  14. if (cl == NULL) {
  15. return NULL;
  16. }
  17. ///然后返回
  18. return cl;
  19. }

接下来就是两个重要的chain,它们其实就是对chain再进行了一次封装。

1 ngx_output_chain_ctx_t , 这个chain主要是管理输出buf。

2 ngx_chain_writer_ctx_t 这个主要是用在upstream模块。

因此我们主要来看ngx_output_chain_ctx_t。

ngx_output_chain_ctx_t,它包含了三种类型的chain,分别是in,free以及busy。

现在来介绍这几个重要的域:

buf : 这个域也就是我们拷贝数据的地方,我们一般输出的话都是从in直接copy相应的size到buf中。

in : 这个就是我们保存那些需要发送数据的地方。

free : 这个保存了一些空的buf,也就是说如果free存在,我们都会直接从free中取buf到前面的buf域。

busy :这个保存了已经发送完毕的buf,也就是每次我们从in中将buf读取完毕后,确定数据已经取完,此时就会将这个chain拷贝到busy中。然后将比较老的busy buf拷贝到free中。

output_filter是一个回调函数,用来过滤输出。

剩下的就是一些标记域。

Java代码  

  1. typedef struct {
  2. ngx_buf_t                   *buf;
  3. ngx_chain_t                 *in;
  4. ngx_chain_t                 *free;
  5. ngx_chain_t                 *busy;
  6. ///相关的标记,是否使用sendfile,是否使用directio等等。。
  7. unsigned                     sendfile:1;
  8. unsigned                     directio:1;
  9. #if (NGX_HAVE_ALIGNED_DIRECTIO)
  10. unsigned                     unaligned:1;
  11. #endif
  12. unsigned                     need_in_memory:1;
  13. unsigned                     need_in_temp:1;
  14. ///内存池。
  15. ngx_pool_t                  *pool;
  16. ///每次从pool中重新alloc一个buf这个值都会相应加一。
  17. ngx_int_t                    allocated;
  18. ngx_bufs_t                   bufs;
  19. ///这个用来标记当前那个模块使用这个chain
  20. ngx_buf_tag_t                tag;
  21. ngx_output_chain_filter_pt   output_filter;
  22. void                        *filter_ctx;
  23. } ngx_output_chain_ctx_t;

它对应的主要是ngx_output_chain函数。这个函数主要功能就是拷贝in chain的数据到buf域中。这个函数很复杂,我们这里简要分析一下:

Java代码  

  1. ngx_int_t
  2. ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in)
  3. {
  4. off_t         bsize;
  5. ngx_int_t     rc, last;
  6. ngx_chain_t  *cl, *out, **last_out;
  7. ...........................................
  8. /* add the incoming buf to the chain ctx->in */
  9. ///拷贝in 到ctx的in chain中。
  10. if (in) {
  11. if (ngx_output_chain_add_copy(ctx->pool, &ctx->in, in) == NGX_ERROR) {
  12. return NGX_ERROR;
  13. }
  14. }
  15. out = NULL;
  16. last_out = &out;
  17. last = NGX_NONE;
  18. ///开始循环处理ctx-in chain.这里有两层循环。
  19. for ( ;; ) {
  20. ///开始遍历in
  21. while (ctx->in) {
  22. ///计算当前in的buf长度。这个长度也就是还没处理的数据长度。
  23. bsize = ngx_buf_size(ctx->in->buf);
  24. ..................................................
  25. ///如果buf为空,则我们需要给buf分配空间。
  26. if (ctx->buf == NULL) {
  27. ///这个函数很简单,主要是处理file buf,如果是file buf则会create一个buf链接到ctx
  28. rc = ngx_output_chain_align_file_buf(ctx, bsize);
  29. if (rc == NGX_ERROR) {
  30. return NGX_ERROR;
  31. }
  32. ///如果是memory buf,都会到这里
  33. if (rc != NGX_OK) {
  34. ///如果free不为空,则我们从free chain中取得buf。
  35. if (ctx->free) {
  36. /* get the free buf */
  37. cl = ctx->free;
  38. ctx->buf = cl->buf;
  39. ctx->free = cl->next;
  40. ngx_free_chain(ctx->pool, cl);
  41. } else if (out || ctx->allocated == ctx->bufs.num) {
  42. break;
  43. }
  44. ///否则我们要重新create一个buf,然后链接到ctx,这里主要buf的大小和in chain的没有处理的数据一样大。
  45. else if (ngx_output_chain_get_buf(ctx, bsize) != NGX_OK) {
  46. return NGX_ERROR;
  47. }
  48. }
  49. }
  50. ///从in chain拷贝数据到buf,并更新相关域。
  51. rc = ngx_output_chain_copy_buf(ctx);
  52. if (rc == NGX_ERROR) {
  53. return rc;
  54. }
  55. if (rc == NGX_AGAIN) {
  56. if (out) {
  57. break;
  58. }
  59. return rc;
  60. }
  61. ///如果size为0,说明in chain中的第一个chain的数据已经被拷贝完了,此时删除这个chain。
  62. if (ngx_buf_size(ctx->in->buf) == 0) {
  63. ctx->in = ctx->in->next;
  64. }
  65. ///重新分配一个 chain
  66. cl = ngx_alloc_chain_link(ctx->pool);
  67. if (cl == NULL) {
  68. return NGX_ERROR;
  69. }
  70. ///链接buf到cl
  71. cl->buf = ctx->buf;
  72. cl->next = NULL;
  73. *last_out = cl;
  74. last_out = &cl->next;
  75. ctx->buf = NULL;
  76. }
  77. if (out == NULL && last != NGX_NONE) {
  78. if (ctx->in) {
  79. return NGX_AGAIN;
  80. }
  81. return last;
  82. }
  83. ///调用回调函数
  84. last = ctx->output_filter(ctx->filter_ctx, out);
  85. if (last == NGX_ERROR || last == NGX_DONE) {
  86. return last;
  87. }
  88. ///update 相关的chain,主要是将刚才copy完的buf 加入到busy chain,然后从busy chain中取出buf放到free chain中。
  89. ngx_chain_update_chains(&ctx->free, &ctx->busy, &out, ctx->tag);
  90. last_out = &out;
  91. }
  92. }

这里我只是简要的分析了下,详细的还需要接合其他模块来看。

最后来看ngx_chain_writer_ctx_t,这个主要用于ustream(由于没看这个模块,因此不理解这里为什么要多出来个writer).大概看了,觉得应该是ustream模块发送的数据量比较大,因此这里通过这个chain来直接调用writev来将数据发送出去。

Java代码  

  1. typedef struct {
  2. ///保存了所要输出的chain。
  3. ngx_chain_t                 *out;
  4. ///这个保存了这次新加入的所需要输出的chain。
  5. ngx_chain_t                **last;
  6. ///这个表示当前连接
  7. ngx_connection_t            *connection;
  8. ngx_pool_t                  *pool;
  9. off_t                        limit;
  10. } ngx_chain_writer_ctx_t;

这里我们要知道out是会变化的。每次输出后,这个都会指向下一个需要发送的chain。

Java代码  

    1. ngx_int_t
    2. ngx_chain_writer(void *data, ngx_chain_t *in)
    3. {
    4. ngx_chain_writer_ctx_t *ctx = data;
    5. off_t              size;
    6. ngx_chain_t       *cl;
    7. ngx_connection_t  *c;
    8. c = ctx->connection;
    9. ///这里将in中的也就是新加如的chain ,全部复制到last中。也就是它保存了最后的数据。
    10. for (size = 0; in; in = in->next) {
    11. ....................................
    12. ///计算大小。
    13. size += ngx_buf_size(in->buf);
    14. cl = ngx_alloc_chain_link(ctx->pool);
    15. if (cl == NULL) {
    16. return NGX_ERROR;
    17. }
    18. ///加入last
    19. cl->buf = in->buf;
    20. cl->next = NULL;
    21. *ctx->last = cl;
    22. ctx->last = &cl->next;
    23. }
    24. ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
    25. "chain writer in: %p", ctx->out);
    26. ///遍历out chain
    27. for (cl = ctx->out; cl; cl = cl->next) {
    28. ///计算所需要输出的大小
    29. size += ngx_buf_size(cl->buf);
    30. }
    31. if (size == 0 && !c->buffered) {
    32. return NGX_OK;
    33. }
    34. ///调用send_chain(一般是writev)来输出out中的数据。
    35. ctx->out = c->send_chain(c, ctx->out, ctx->limit);
    36. ........................
    37. return NGX_AGAIN;
    38. }
时间: 2024-10-07 02:12:15

nginx的内存管理的相关文章

Nginx源码研究二:NGINX的内存管理

关于nginx的内存使用,我们先看代码,下面是nginx_cycle.c中对全局数据结构cycle的初始化过程 pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log); //申请16K的内存池 if (pool == NULL) { return NULL; } pool->log = log; cycle = ngx_pcalloc(pool, sizeof(ngx_cycle_t)); if (cycle == NULL) { ngx_destroy

Nginx Slab内存管理

L38  Slub内存管理适用 ngx_http_limit_conn_module.ngx_http_limit_req_module 模块场景 原文地址:https://www.cnblogs.com/jackey2015/p/10684151.html

nginx源码分析—内存池结构ngx_pool_t及内存管理

Content 0. 序 1. 内存池结构 1.1 ngx_pool_t结构 1.2 其他相关结构 1.3 ngx_pool_t的逻辑结构 2. 内存池操作 2.1 创建内存池 2.2 销毁内存池 2.3 重置内存池 2.4 分配内存 2.4.1 ngx_palloc()函数分析 2.4.2 ngx_palloc_block()函数分析 2.5 释放内存 2.6 注册cleanup 2.7 内存池的物理结构 3. 一个例子 3.1 代码 3.2 如何编译 3.3 运行结果 4. 小结 5. 致谢

TCMalloc优化Nginx、MySQL、Redis内存管理

TCMalloc(Thread-Caching Malloc)与标准glibc库的malloc实现一样的功能,但是TCMalloc在效率和速度效率都比标准malloc高很多.TCMalloc是google-perftools工具中的一个(gperftools四个工具分别是:TCMalloc.heap-checker.heap-profiler和cpu-profiler),这个工具是开源的,以源码形式发布.如果觉得自己维护一个内存分配器麻烦的话,可以考虑将TCMalloc静态库连接到你的程序中.使

nginx 学习四 内存池 ngx_pool_t 和内存管理操作

这几天在看nginx,发现凡是有内存申请的地方都有pool这个东东出现,仔细看看,原来pool的类型是ngx_pool_t,是nginx用来做内存管理的,于是就决定看看他的实现. 1 nginx内存池相关的结构体 ngx_pool_t定义在core/ngx_palloc.h ngx_palloc.c中,下面是几个主要的结构体 ngx_pool_data_t typedef struct { //内存池的数据结构模块 u_char *last; //当前内存分配结束位置,即下一段可分配内存的起始位

PHP扩展-生命周期和内存管理

1. PHP源码结构 PHP的内核子系统有两个,ZE(Zend Engine)和PHP Core.ZE负责将PHP脚本解析成机器码(也成为token符)后,在进程空间执行这些机器码:ZE还负责内存管理,变量作用域管理和对PHP函数的调度管理. PHP Core负责和SAPI层的通信:PHP Core也为safe_mode, open_basedir的检查提供了统一控制层:PHP Core还提供了streams层,用于用户域的文件和网络IO操作.其中SAPI(Server Application

内存管理之内存池概述(转)

原文链接:http://www.xiaoyaochong.net/wordpress/index.php/2013/08/10/%E5%BC%95%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E4%B9%8B%E5%86%85%E5%AD%98%E6%B1%A0%E6%A6%82%E8%BF%B0/ 在我们编写代码的过程中,不可避免的要和内存打交道,在申请释放不太频繁的情况下,通常让系统进行内存管理即可.但是,直接使用系统调用malloc/free.new/delet

linux内存管理

一.Linux 进程在内存中的数据结构 一个可执行程序在存储(没有调入内存)时分为代码段,数据段,未初始化数据段三部分:    1) 代码段:存放CPU执行的机器指令.通常代码区是共享的,即其它执行程序可调用它.假如机器中有数个进程运行相同的一个程序,那么它们就可以使用同一个代码段.     2) 数据段:存放已初始化的全局变量.静态变量(包括全局和局部的).常量.static全局变量和static函数只能在当前文件中被调用.     3) 未初始化数据区(uninitializeddata s

Block内存管理实例分析

在ios开发中,相信说道block大家都不陌生,内存管理问题也是开发者最头疼的问题,网上很多讲block的博客,但大都是理论性多点,今天结合一些实例来讲解下. 存储域 首先和大家聊聊block的存储域,根据block在内存中的位置,block被分为三种类型: NSGlobalBlock NSStackBlock NSMallocBlock 从字面意思上大家也可以看出来 NSGlobalBlock是位于全局区的block,它是设置在程序的数据区域(.data区)中. NSStackBlock是位于