先来看内存池的实现,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. }

转载于:https://www.cnblogs.com/405845829qq/p/4398049.html

nginx的内存管理相关推荐

  1. gperftools mysql_利用 gperftools 对nginx mysql 内存管理 性能优化

    利用 gperftools 对nginx 与 mysql  进行 内存管理  性能优化 降低负载. Gperftools 是由谷歌开发.官方对gperftools 的介绍为: These tools ...

  2. nginx slab内存管理机制

    nginx的请求分布在不同的进程,如果进程间需要交互各种不同大小的对象, 需要共享一些复杂的数据结构, 如链表. 树. 图等, nginx实现了一套高效的slab内存管理机制, 可以帮助我们快速实现多 ...

  3. Nginx Slab内存管理

    L38  Slub内存管理适用 ngx_http_limit_conn_module.ngx_http_limit_req_module 模块场景 我们可以用阿里第三方模块Slab_Stat模块 并且 ...

  4. Jemalloc优化MySQL、Nginx/Tengine内存管理

    Jemalloc源于Jason Evans 2006年在BSDcan conference发表的论文:<A Scalable Concurrent malloc Implementation f ...

  5. nginx的内存池及内存管理

    nginx对内存的管理是由自己实现的内存池结构ngx_pool_t来完成,本文主要讲nginx的内存管理. nginx对内存管理涉及到四个文件:src/core/ngx_palloc.h.src/co ...

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

    本博客( http://blog.csdn.net/livelylittlefish)贴出作者(阿波)相关研究.学习内容所做的笔记,欢迎广大朋友指正! Content 0.序 1.内存池结构 1.1 ...

  7. 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重置内存 ...

  8. Nginx内存管理详解

    目录: 1.Nginx内存管理介绍 2.Nginx内存池的逻辑结构 3.Nginx内存池的基本数据结构 4.内存池基本操作介绍 5.内存池管理源码详解 6.内存池使用源码详解 7.小结 1.Nginx ...

  9. 结合源码看nginx-1.4.0之nginx内存管理详解

    目录 0. 摘要 1. nginx内存结构设计 2. nginx内存数据结构 3. nginx内存管理原理 4. 一个简单的内存模型 5. 小结 6. 参考资料 0. 摘要 内存管理,是指软件运行时对 ...

最新文章

  1. 软件测试黑盒测试实验心得_软件测试的基础知识
  2. Python匿名函数:lamdba()函数
  3. 在ESXi5,1,ESXi5.5安装异步驱动程序
  4. 百度AI学习:一、语音识别
  5. C语言中定义整形可以连等吗,关于一道分解整数为N个连数整数的编程题
  6. 使用bcp进行大数据量导出导入
  7. Python 进阶 —— 可变参数(*args, **kw)与参数收集的逆过程
  8. Tomcat报错:ERROR:transport error 202: gethostbyname: unknown host
  9. STM32CubeMX+Keil裸机代码风格(2)
  10. c++下字符串分割函数split实现
  11. 2021年房地产经纪行业发展研究报告
  12. 【测试】对手机拍照测试用例的设计
  13. 数据库-在E-R模型中,如果有5个不同的实体集,存在2个1:n联系和3个m:n联系,根据E-R模型转换为关系模型的规则,该E-R图转换为关系模式的数目至少
  14. php进程间通信 yoc_php 进程间通信 ipc
  15. 什么是jsp,什么是Servlet?jsp和Servlet有什么区别?
  16. 安格鲁貂出现感冒如何解决?
  17. 诺亚传说手游怎么用电脑玩 诺亚传说手游PC电脑版教程
  18. java hashtable 数据结构_java数据结构——哈希表(HashTable)
  19. DataSheet查询网站
  20. 项目一、调用百度地图api实现电子围栏和报警信息关联

热门文章

  1. [深度学习-总结]LeNet网络的权重的大小的计算
  2. [软件工程-设计模式] GRASP软件设计的模式和原则
  3. mybatis注解开发_快速搭建MyBatis开发环境(配置版+注解版)
  4. 吴恩达深度学习 —— 2.18(选修)逻辑回归损失函数的解释
  5. K-Means原理详解与Java代码实现细节
  6. Golang基本变量
  7. Struts使用细节
  8. QT【001】- 基础写在前面的话
  9. v-viewer图片打不开一直在刷新_python实现将一组图片转化成视频
  10. 字典推导式_Python基础-推导式