nginx基础设施

内存池

简介:

Nginx里内存的使用大都十分有特色:申请了永久保存,抑或伴随着请求的结束而全部释放,还有写满了缓冲再从头接着写.这么做的原因也主要取决于Web Server的特殊的场景,内存的分配和请求相关,一条请求处理完毕,即可释放其相关的内存池,降低了开发中对内存资源管理的复杂度,也减少了内存碎片的存在.

所以在Nginx使用内存池时总是只申请,不释放,使用完毕后直接destroy整个内存池.我们来看下内存池相关的实现。

结构:

  1. struct ngx_pool_s {
  2. ngx_pool_data_t d;
  3. size_t max;
  4. ngx_pool_t *current;
  5. ngx_chain_t *chain;
  6. ngx_pool_large_t *large;
  7. ngx_pool_cleanup_t *cleanup;
  8. ngx_log_t *log;
  9. };
  10.  
  11. struct ngx_pool_large_s {
  12. ngx_pool_large_t *next;
  13. void *alloc;
  14. };
  15.  
  16. typedef struct {
  17. u_char *last;
  18. u_char *end;
  19. ngx_pool_t *next;
  20. ngx_uint_t failed;
  21. } ngx_pool_data_t;

内存池

实现:

这三个数据结构构成了基本的内存池的主体.通过ngx_create_pool可以创建一个内存池,通过ngx_palloc可以从内存池中分配指定大小的内存。

  1. ngx_pool_t *
  2. ngx_create_pool(size_t size, ngx_log_t *log)
  3. {
  4. ngx_pool_t *p;
  5.  
  6. p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);
  7. if (p == NULL) {
  8. return NULL;
  9. }
  10.  
  11. p->d.last = (u_char *) p + sizeof(ngx_pool_t);
  12. p->d.end = (u_char *) p + size;
  13. p->d.next = NULL;
  14. p->d.failed = 0;
  15.  
  16. size = size - sizeof(ngx_pool_t);
  17. p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;
  18.  
  19. p->current = p;
  20. p->chain = NULL;
  21. p->large = NULL;
  22. p->cleanup = NULL;
  23. p->log = log;
  24.  
  25. return p;
  26. }

这里首申请了一块大小为size的内存区域,其前sizeof(ngx_pool_t)字节用来存储ngx_pool_t这个结构体自身自身.所以若size小于sizeof(ngx_pool_t)将会有coredump的可能性。

我们常用来分配内存的有三个接口:ngx_palloc,ngx_pnalloc,ngx_pcalloc。

分别来看下它们的实现:

  1. void *
  2. ngx_palloc(ngx_pool_t *pool, size_t size)
  3. {
  4. u_char *m;
  5. ngx_pool_t *p;
  6.  
  7. if (size <= pool->max) {
  8.  
  9. p = pool->current;
  10.  
  11. do {
  12. m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);
  13.  
  14. if ((size_t) (p->d.end - m) >= size) {
  15. p->d.last = m + size;
  16.  
  17. return m;
  18. }
  19.  
  20. p = p->d.next;
  21.  
  22. } while (p);
  23.  
  24. return ngx_palloc_block(pool, size);
  25. }
  26.  
  27. return ngx_palloc_large(pool, size);
  28. }
  29.  
  30.  
  31. void *
  32. ngx_pnalloc(ngx_pool_t *pool, size_t size)
  33. {
  34. u_char *m;
  35. ngx_pool_t *p;
  36.  
  37. if (size <= pool->max) {
  38.  
  39. p = pool->current;
  40.  
  41. do {
  42. m = p->d.last;
  43.  
  44. if ((size_t) (p->d.end - m) >= size) {
  45. p->d.last = m + size;
  46.  
  47. return m;
  48. }
  49.  
  50. p = p->d.next;
  51.  
  52. } while (p);
  53.  
  54. return ngx_palloc_block(pool, size);
  55. }
  56.  
  57. return ngx_palloc_large(pool, size);
  58. }
  59.  
  60.  
  61. void *
  62. ngx_pcalloc(ngx_pool_t *pool, size_t size)
  63. {
  64. void *p;
  65.  
  66. p = ngx_palloc(pool, size);
  67. if (p) {
  68. ngx_memzero(p, size);
  69. }
  70.  
  71. return p;
  72. }

ngx_pcalloc其只是ngx_palloc的一个封装,将申请到的内存全部初始化为0。

ngx_palloc相对ngx_pnalloc,其会将申请的内存大小向上扩增到NGX_ALIGNMENT的倍数,以方便内存对齐,减少内存访问次数。

Nginx的内存池不仅用于内存方面的管理,还可以通过ngx_pool_cleanup_add来添加内存池释放时的回调函数,以便用来释放自己申请的其他相关资源。

从代码中可以看出,这些由自己添加的释放回调是以链表形式保存的,也就是说你可以添加多个回调函数来管理不同的资源。

共享内存

slab算法

buffer管理

buffer重用机制

buffer防拷贝机制

chain管理

chain重用机制

aio原理

锁实现

基本数据结构

时间缓存

文件缓存

log机制