nginx源码分析--内存池
来源:互联网 发布:网易域名邮箱smtp 编辑:程序博客网 时间:2024/06/08 05:15
内存池是nginx很重要的数据结构,结构图大概如下。
ngx_palloc.h
/* * Copyright (C) Igor Sysoev * Copyright (C) Nginx, Inc. */#ifndef _NGX_PALLOC_H_INCLUDED_#define _NGX_PALLOC_H_INCLUDED_#include <ngx_config.h>#include <ngx_core.h>/* * NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86. * On Windows NT it decreases a number of locked pages in a kernel. */#define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1)#define NGX_DEFAULT_POOL_SIZE (16 * 1024)#define NGX_POOL_ALIGNMENT 16#define NGX_MIN_POOL_SIZE \ ngx_align((sizeof(ngx_pool_t) + 2 * sizeof(ngx_pool_large_t)), \ NGX_POOL_ALIGNMENT)// 函数指针,返回值为void,参数为void型的指针typedef void (*ngx_pool_cleanup_pt)(void *data);typedef struct ngx_pool_cleanup_s ngx_pool_cleanup_t;/* nginx在释放pool内存时会一次调用ngx_pool_cleanup_s链上各个节点的handler函数,并且以对应的data作为参数。*/struct ngx_pool_cleanup_s { // 回调函数 ngx_pool_cleanup_pt handler; // 执行handler时传入的参数 void *data; ngx_pool_cleanup_t *next;};typedef struct ngx_pool_large_s ngx_pool_large_t;// 管理大内存的结构体,nginx分配的内存分为大内存和小内存,ngx_pool_large_s管理大内存的操作,多少为大内存下面会说struct ngx_pool_large_s { // 下一个ngx_pool_large_s节点,如果某个ngx_pool_large_t节点内存不够,则向下找 ngx_pool_large_t *next; // 指向可用内存的指针,这些内存在分配大内存时使用 void *alloc;};// 管理小内存的结构体typedef struct { // 指向可用内存的首地址,表示从这开始的内存是可分配的,last之前的内存是已经被分配出去了的 u_char *last; // 指向可用内存的末地址,end之前,last之后的内存是可用的,超过end的内存是不可被分配的 u_char *end; // 下一个分配内存的节点,同ngx_pool_large_t ngx_pool_t *next; // 在该节点管理的内存中,分配内存时失败的次数 ngx_uint_t failed;} ngx_pool_data_t;struct ngx_pool_s { ngx_pool_data_t d; // max的值是大小内存的分界线 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 { ngx_fd_t fd; u_char *name; ngx_log_t *log;} ngx_pool_cleanup_file_t;void *ngx_alloc(size_t size, ngx_log_t *log);void *ngx_calloc(size_t size, ngx_log_t *log);ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);void ngx_destroy_pool(ngx_pool_t *pool);void ngx_reset_pool(ngx_pool_t *pool);void *ngx_palloc(ngx_pool_t *pool, size_t size);void *ngx_pnalloc(ngx_pool_t *pool, size_t size);void *ngx_pcalloc(ngx_pool_t *pool, size_t size);void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size);void ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd);void ngx_pool_cleanup_file(void *data);void ngx_pool_delete_file(void *data);#endif /* _NGX_PALLOC_H_INCLUDED_ */
ngx_palloc.c
/* * Copyright (C) Igor Sysoev * Copyright (C) Nginx, Inc. */#include <ngx_config.h>#include <ngx_core.h>static void *ngx_palloc_block(ngx_pool_t *pool, size_t size);static void *ngx_palloc_large(ngx_pool_t *pool, size_t size);/* 创建一个内存池 @param size 内存池大小 @param log 对应的日志结构体指针 @return p 内存池的首地址*/ngx_pool_t * ngx_create_pool(size_t size, ngx_log_t *log){ ngx_pool_t *p; // 内存对齐,提高cpu取数据时的性能 p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log); if (p == NULL) { return NULL; } // 内存池已经分配的内存末地址,也是可分配内存的首地址,前面需要存储内存池元数据的结构体ngx_pool_t 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); // 小于max的内存在ngx_pool_data_t管理的内存块分配,大于max的内存在ngx_pool_large_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;}/* 销毁内存池函数*/void ngx_destroy_pool(ngx_pool_t *pool){ ngx_pool_t *p, *n; ngx_pool_large_t *l; ngx_pool_cleanup_t *c; // 销毁内存池之前逐个调用cleanup链表的节点中的handler函数,并且以data作为参数,见ngx_pool_cleanup_s结构体 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; } }}/* 重置内存池状态*/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); } } // 把管理大块内存的结构体指针置空 pool->large = NULL; // 把之前分配出去的内存回收回来,主要是修改last的值即可,因为分配内存是通过last的值去完成的 for (p = pool; p; p = p->d.next) { p->d.last = (u_char *) p + sizeof(ngx_pool_t); }}/* 在内存池上分配内存 @param pool 用来分配内存的内存池 @param size 分配的大小 @return p 返回分配内存成功后的首地址*/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); /* 该节点上的内存是否能满足所需,也就是所剩内存大小是否大于等于所需的内存size, 是的话把last往后移动size大小即可 */ if ((size_t) (p->d.end - m) >= size) { p->d.last = m + size; return m; } // 如果当前节点的内存不足,到下一个节点上去找,直到都不能满足 p = p->d.next; } while (p); // 内存池上的可用内存都小于size大小,分配一块新的内存,并用ngx_pool_s结构管理,然后追加到ngx_pool_data_t链表中 return ngx_palloc_block(pool, size); } // 如果所需的内存是大块内存,就在大块内存中分配 return ngx_palloc_large(pool, size);}// 不做对齐处理,参考上面的ngx_palloc函数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_pool_t结构体管理, 然后链到ngx_pool_t结构体链表中,最后在该内存中分配size大小的内存 @return 返回分配了size大小的内存后,分配出去的内存的首地址 */static void * ngx_palloc_block(ngx_pool_t *pool, size_t size){ u_char *m; size_t psize; ngx_pool_t *p, *new, *current; // 分配一块和之前节点内存大小一样的内存,这个值是创建内存池时就决定了 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; // 可用内存等于end-ngx_pool_data_t结构体的大小,ngx_pool_data_t是描述这块内存的元数据 m += sizeof(ngx_pool_data_t); m = ngx_align_ptr(m, NGX_ALIGNMENT); // 分配size大小的内存出去,并修改last指针 new->d.last = m + size; current = pool->current; /* 这里把cuurent指针指向最后一个分配内存失败次数大于4的节点的后面一个节点。这里认为, 分配失败次数大于4的内存块,以后分配失败的概率也更大,所以这里做了优化。 修改current指针,该指针是分配内存时,查找的起点。这样会优化查找的性能, 因为这是新开辟的内存,大部分情况下可以满足后面的内存分配,提高查找速度。 但是之所以会新开辟新的一块内存,并不是说之前节点上的内存就被分配完了, 可能只是不能满足这次的内存分配而已,比如前面节点还有1M,但是这次需要2M的内存,那么 就需要创建一个新的内存节点完成分配,那么下次需要分配1M内存的时候,也会从新开辟的节 点上去找,而不会再去旧节点上查找,这样就会导致那块内存的浪费。这里是空间换时间。 */ for (p = current; p->d.next; p = p->d.next) { if (p->d.failed++ > 4) { current = p->d.next; } } // 把新开辟的内存追加到原来的ngx_pool_data_t链表上 p->d.next = new; pool->current = current ? current : new; return m;}/* 分配大块内存,不做对齐处理 @return 返回size大小的内存的首地址*/static void * ngx_palloc_large(ngx_pool_t *pool, size_t size){ void *p; ngx_uint_t n; ngx_pool_large_t *large; // 分配一块大内存 p = ngx_alloc(size, pool->log); if (p == NULL) { return NULL; } n = 0; for (large = pool->large; large; large = large->next) { // 找到一个alloc为空的ngx_pool_large_s结构体,然后把p对应的内存给他管理 if (large->alloc == NULL) { large->alloc = p; return p; } // 查找三次后都没有找到,则新建一个ngx_pool_large_s结构体来对p对应的内存进行管理 if (n++ > 3) { break; } } // 申请一个ngx_pool_large_s结构体大小的内存 large = ngx_palloc(pool, sizeof(ngx_pool_large_t)); if (large == NULL) { // 申请失败的话要把p对应的内存释放掉 ngx_free(p); return NULL; } // 头插法 把larger结构体查到最前面 large->alloc = p; large->next = pool->large; pool->large = large; return p;}/* 分配大块内存,做对齐处理,直接插入到ngx_pool_large_t链表中 @return 返回size大小的内存的首地址*/void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment){ void *p; ngx_pool_large_t *large; p = ngx_memalign(alignment, size, pool->log); if (p == NULL) { return NULL; } large = ngx_palloc(pool, sizeof(ngx_pool_large_t)); if (large == NULL) { ngx_free(p); return NULL; } large->alloc = p; large->next = pool->large; pool->large = large; return p;}/* 释放全部大块内存,把alloc指针置空*/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;}/* 分配size大小的内存,并且用0初始化*/void * ngx_pcalloc(ngx_pool_t *pool, size_t size){ void *p; p = ngx_palloc(pool, size); if (p) { ngx_memzero(p, size); } return p;}/* 分配一个ngx_pool_cleanup_t结构体和size大小的空间,然后在ngx_pool_cleanup_t链用头插法插入该节点 @return 返回ngx_pool_cleanup_t节点指针*/ngx_pool_cleanup_t * ngx_pool_cleanup_add(ngx_pool_t *p, size_t size){ ngx_pool_cleanup_t *c; // 分配一个ngx_pool_cleanup_t结构体空间 c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t)); if (c == NULL) { return NULL; } // 如果有size则分配size大小的空间供存储数据,否则把data指针置空 if (size) { c->data = ngx_palloc(p, size); if (c->data == NULL) { return NULL; } } else { c->data = NULL; } // 初始化handler c->handler = NULL; /* 用头插法插入该节点,因为释放内存池时会逐个调用ngx_pool_cleanup_t节点,而ngx_pool_cleanup_t节点 的位置是无关紧要的,所以这里用头插法插入该节点,提高性能,没必要查到链表最后。 */ c->next = p->cleanup; p->cleanup = c; ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "add cleanup: %p", c); return c;}/* 关闭某个fd对应的文件 @param fd 文件描述符相关的*/void ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd){ ngx_pool_cleanup_t *c; ngx_pool_cleanup_file_t *cf; // 遍历ngx_pool_cleanup_t链表,找到handler等于ngx_pool_cleanup_file函数并且data中的fd等于传入的fd的节点。然后执行handler for (c = p->cleanup; c; c = c->next) { if (c->handler == ngx_pool_cleanup_file) { cf = c->data; if (cf->fd == fd) { c->handler(cf); c->handler = NULL; return; } } }}/* 关闭某个文件*/void ngx_pool_cleanup_file(void *data){ ngx_pool_cleanup_file_t *c = data; ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d", c->fd); if (ngx_close_file(c->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, ngx_close_file_n " \"%s\" failed", c->name); }}/* 删除和关闭某个文件*/void ngx_pool_delete_file(void *data){ ngx_pool_cleanup_file_t *c = data; ngx_err_t err; ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d %s", c->fd, c->name); if (ngx_delete_file(c->name) == NGX_FILE_ERROR) { err = ngx_errno; if (err != NGX_ENOENT) { ngx_log_error(NGX_LOG_CRIT, c->log, err, ngx_delete_file_n " \"%s\" failed", c->name); } } if (ngx_close_file(c->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, ngx_close_file_n " \"%s\" failed", c->name); }}
0 0
- Nginx源码分析-内存池
- Nginx源码分析-内存池
- Nginx源码分析-内存池
- Nginx源码分析-内存池
- Nginx源码分析-内存池
- Nginx源码分析-内存池
- Nginx源码分析-内存池
- Nginx源码分析-内存池
- Nginx源码分析-内存池
- Nginx源码分析-内存池
- Nginx源码分析-内存池
- nginx源码分析--内存池
- Nginx内存池实现源码分析
- [nginx源码分析]ngx内存池实现
- nginx源码分析--内存池ngx_poll_t 内存池管理
- Nginx源码分析---内存池结构ngx_pool_t及内存管理
- 服务器数据库系列 - Nginx源码分析-内存池
- Nginx高级数据结构源码分析(四)-----内存池
- 如鹏java学习进程 模拟小球弹跳
- python基础知识-元组和字符串
- View
- iOS UIPageControl使用
- 一、BIO、NIO、AIO通信机制理解
- nginx源码分析--内存池
- 移动窗口
- 1548 欧姆诺姆和糖果 51NOD
- tcl安装与学习
- 工业大数据漫谈8:工业大数据的采集
- spark 2.0 metrics example of spark sink -- JmxSink
- 图片圆角实现
- Android ScrollView中的ListView只显示一个条目问题
- alter system flush buffer_cache