Nginx源码分析(2)-数据结构-ngx_pool_t

1. 数据结构:

//清理回调
struct ngx_pool_cleanup_s {
    ngx_pool_cleanup_pt   handler; //回调函数
    void                 *data; //清理的数据指针
    ngx_pool_cleanup_t   *next; //下一个清理结构
};

typedef struct ngx_pool_large_s  ngx_pool_large_t;

//大数据块
struct ngx_pool_large_s {
    ngx_pool_large_t     *next; //下一个块的位置
    void                 *alloc; //当前块指针
};

//内存池数据块
typedef struct {
    u_char               *last; //当前分配到的内存位置
    u_char               *end; //数据块的结束位置
    ngx_pool_t           *next; //下一个数据块的位置
    ngx_uint_t            failed; //查找内存失败次数
} ngx_pool_data_t;

//内存池结构
struct ngx_pool_s {
    ngx_pool_data_t       d; //数据块
    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;
};

2. 关系简图:

3. 源码注释分析:

  1 //创建内存池
  2 ngx_pool_t *
  3 ngx_create_pool(size_t size, ngx_log_t *log)
  4 {
  5     ngx_pool_t  *p;
  6
  7     //分配pool内存
  8     p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);
  9     if (p == NULL) {
 10         return NULL;
 11     }
 12
 13     //设置数据块当分配位置
 14     p->d.last = (u_char *) p + sizeof(ngx_pool_t);
 15     //设置数据块结束位置
 16     p->d.end = (u_char *) p + size;
 17     p->d.next = NULL;
 18     p->d.failed = 0;
 19
 20     //设置内存池的最大内存空间
 21     size = size - sizeof(ngx_pool_t);
 22     p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;
 23
 24     //设置current指向当前数据块
 25     p->current = p;
 26     p->chain = NULL;
 27     p->large = NULL;
 28     p->cleanup = NULL;
 29     p->log = log;
 30
 31     return p;
 32 }
 33
 34 //销毁内存池
 35 void
 36 ngx_destroy_pool(ngx_pool_t *pool)
 37 {
 38     ngx_pool_t          *p, *n;
 39     ngx_pool_large_t    *l;
 40     ngx_pool_cleanup_t  *c;
 41
 42     //调用注册的清理函数清理内存
 43     for (c = pool->cleanup; c; c = c->next) {
 44         if (c->handler) {
 45             ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
 46                            "run cleanup: %p", c);
 47             c->handler(c->data);
 48         }
 49     }
 50
 51     //释放大块内存
 52     for (l = pool->large; l; l = l->next) {
 53
 54         ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc);
 55
 56         if (l->alloc) {
 57             ngx_free(l->alloc);
 58         }
 59     }
 60
 61 #if (NGX_DEBUG)
 62
 63     /*
 64      * we could allocate the pool->log from this pool
 65      * so we cannot use this log while free()ing the pool
 66      */
 67
 68     for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
 69         ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
 70                        "free: %p, unused: %uz", p, p->d.end - p->d.last);
 71
 72         if (n == NULL) {
 73             break;
 74         }
 75     }
 76
 77 #endif
 78
 79     //释放内存池结构内存
 80     for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
 81         ngx_free(p);
 82
 83         if (n == NULL) {
 84             break;
 85         }
 86     }
 87 }
 88
 89 //重置内存池
 90 void
 91 ngx_reset_pool(ngx_pool_t *pool)
 92 {
 93     ngx_pool_t        *p;
 94     ngx_pool_large_t  *l;
 95
 96     //释放大块内存
 97     for (l = pool->large; l; l = l->next) {
 98         if (l->alloc) {
 99             ngx_free(l->alloc);
100         }
101     }
102
103     pool->large = NULL;
104
105     //每个数据块均保留头部信息,分配清0
106     for (p = pool; p; p = p->d.next) {
107         p->d.last = (u_char *) p + sizeof(ngx_pool_t);
108     }
109 }
110
111
112 //对齐内存分配
113 void *
114 ngx_palloc(ngx_pool_t *pool, size_t size)
115 {
116     u_char      *m;
117     ngx_pool_t  *p;
118
119     if (size <= pool->max) {
120
121         p = pool->current;
122
123         do {
124             //找到内存对齐地址位置
125             m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);
126
127             //如果尚有内存未分配,则移动分配指针,返回分配地址
128             if ((size_t) (p->d.end - m) >= size) {
129                 p->d.last = m + size;
130
131                 return m;
132             }
133
134             //没有可分配内存则继续找下一个数据块
135             p = p->d.next;
136
137         } while (p);
138
139         //查找空余数据块失败,则分配新数据块
140         return ngx_palloc_block(pool, size);
141     }
142
143     //超过内存池限制则动态分配大块内存
144     return ngx_palloc_large(pool, size);
145 }
146
147 //非对齐分配内存
148 void *
149 ngx_pnalloc(ngx_pool_t *pool, size_t size)
150 {
151     u_char      *m;
152     ngx_pool_t  *p;
153
154     //同样循环查找各个数据块的可用内存
155     if (size <= pool->max) {
156
157         p = pool->current;
158
159         do {
160             m = p->d.last;
161
162             if ((size_t) (p->d.end - m) >= size) {
163                 p->d.last = m + size;
164
165                 return m;
166             }
167
168             p = p->d.next;
169
170         } while (p);
171
172         //分配新的数据块
173         return ngx_palloc_block(pool, size);
174     }
175
176     //分配大数据块
177     return ngx_palloc_large(pool, size);
178 }
179
180 //分配数据块
181 static void *
182 ngx_palloc_block(ngx_pool_t *pool, size_t size)
183 {
184     u_char      *m;
185     size_t       psize;
186     ngx_pool_t  *p, *new, *current;
187
188     //取得内存池大小
189     psize = (size_t) (pool->d.end - (u_char *) pool);
190
191     //对齐分配内存
192     m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);
193     if (m == NULL) {
194         return NULL;
195     }
196
197     //设置标记
198     new = (ngx_pool_t *) m;
199
200     new->d.end = m + psize;
201     new->d.next = NULL;
202     new->d.failed = 0;
203
204     //跳过头部,对齐内存,分配内存,设置标记
205     m += sizeof(ngx_pool_data_t);
206     m = ngx_align_ptr(m, NGX_ALIGNMENT);
207     new->d.last = m + size;
208
209     //如果前面数据块已经失败5次了 则后续不在查找这些数据块的可用内存了
210     current = pool->current;
211
212     for (p = current; p->d.next; p = p->d.next) {
213         if (p->d.failed++ > 4) {
214             current = p->d.next;
215         }
216     }
217
218     //数据块加入到链表
219     p->d.next = new;
220
221     //设置current查找内存开始块
222     pool->current = current ? current : new;
223
224     return m;
225 }
226
227 //分配大块数据内存
228 static void *
229 ngx_palloc_large(ngx_pool_t *pool, size_t size)
230 {
231     void              *p;
232     ngx_uint_t         n;
233     ngx_pool_large_t  *large;
234
235     p = ngx_alloc(size, pool->log);
236     if (p == NULL) {
237         return NULL;
238     }
239
240     n = 0;
241
242     //查找大内存卡链接位置
243     for (large = pool->large; large; large = large->next) {
244         if (large->alloc == NULL) {
245             large->alloc = p;
246             return p;
247         }
248
249         //如果3次没找到结尾则跳出新建一个放在头部
250         if (n++ > 3) {
251             break;
252         }
253     }
254
255     //分配大内存头部,连接数据,加入到链表
256     large = ngx_palloc(pool, sizeof(ngx_pool_large_t));
257     if (large == NULL) {
258         ngx_free(p);
259         return NULL;
260     }
261
262     large->alloc = p;
263     large->next = pool->large;
264     pool->large = large;
265
266     return p;
267 }
268
269 //按照指定内存对齐分配大数据块内存,加入到大块内存链表首部
270 void *
271 ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment)
272 {
273     void              *p;
274     ngx_pool_large_t  *large;
275
276     //分配内存
277     p = ngx_memalign(alignment, size, pool->log);
278     if (p == NULL) {
279         return NULL;
280     }
281
282     //分配大块内存头部
283     large = ngx_palloc(pool, sizeof(ngx_pool_large_t));
284     if (large == NULL) {
285         ngx_free(p);
286         return NULL;
287     }
288
289     //加入到链表
290     large->alloc = p;
291     large->next = pool->large;
292     pool->large = large;
293
294     return p;
295 }
296
297 //大内存块释放
298 ngx_int_t
299 ngx_pfree(ngx_pool_t *pool, void *p)
300 {
301     ngx_pool_large_t  *l;
302
303     for (l = pool->large; l; l = l->next) {
304         if (p == l->alloc) {
305             ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
306                            "free: %p", l->alloc);
307             ngx_free(l->alloc);
308             l->alloc = NULL;
309
310             return NGX_OK;
311         }
312     }
313
314     return NGX_DECLINED;
315 }
316
317 //分配内存并初始化为0
318 void *
319 ngx_pcalloc(ngx_pool_t *pool, size_t size)
320 {
321     void *p;
322
323     p = ngx_palloc(pool, size);
324     if (p) {
325         ngx_memzero(p, size);
326     }
327
328     return p;
329 }
330
331 //添加清理函数
332 ngx_pool_cleanup_t *
333 ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)
334 {
335     ngx_pool_cleanup_t  *c;
336
337     c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t));
338     if (c == NULL) {
339         return NULL;
340     }
341
342     //根据size判断是否需要在此为data分配内存空间
343     if (size) {
344         c->data = ngx_palloc(p, size);
345         if (c->data == NULL) {
346             return NULL;
347         }
348
349     } else {
350         c->data = NULL;
351     }
352
353     c->handler = NULL;
354     c->next = p->cleanup;
355
356     p->cleanup = c;
357
358     ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "add cleanup: %p", c);
359
360     return c;
361 }
时间: 2024-08-26 08:12:25

Nginx源码分析(2)-数据结构-ngx_pool_t的相关文章

Nginx源码分析:3张图看懂启动及进程工作原理

编者按:高可用架构分享及传播在架构领域具有典型意义的文章,本文由陈科在高可用架构群分享.转载请注明来自高可用架构公众号「ArchNotes」.   导读:很多工程师及架构师都希望了解及掌握高性能服务器开发,阅读优秀源代码是一种有效的方式,nginx 是业界知名的高性能 Web 服务器实现,如何有效的阅读及理解 nginx?本文用图解的方式帮助大家来更好的阅读及理解 nginx 关键环节的实现.   陈科,十年行业从业经验,曾在浙江电信.阿里巴巴.华为.五八同城任开发工程及架构师等职,目前负责河狸

nginx源码分析--nginx模块解析

nginx的模块非常之多,可以认为所有代码都是以模块的形式组织,这包括核心模块和功能模块,针对不同的应用场合,并非所有的功能模块都要被用到,附录A给出的是默认configure(即简单的http服务器应用)下被连接的模块,这里虽说是模块连接,但nginx不会像apache或lighttpd那样在编译时生成so动态库而在程序执行时再进行动态加载,nginx模块源文件会在生成nginx时就直接被编译到其二进制执行文件中,所以如果要选用不同的功能模块,必须对nginx做重新配置和编译.对于功能模块的选

[nginx] nginx源码分析--健康检查模块锁分析

健康检查模块 见前文:[nginx] nginx源码分析--健康检查模块 其中有一张框架图, 接下来的内容,将会利用到这个图中的内容. [classic_tong @ https:////www.cnblogs.com/hugetong/p/12274125.html ]  描述 我们知道nginx是多进程的,每个进程都保存了相同的配置.但是实际上, 并不需要每一个进程对每一个后端服务器进行. 于是健康检查模块在这里需要一个进程间同步机制,用来协商哪一个进程对 哪一个后端服务器进行检查. 接下来

nginx源码分析--进程间通信机制 &amp; 同步机制

Nginx源码分析-进程间通信机制 从nginx的进程模型可以知道,master进程和worker进程需要通信,nginx中通信的方式有套接字.共享内存.信号.对于master进程,从外部接受信号,master进程主要就是监控.接受外部信号,将有必要的信号传递给worker进程,master进程大部分时间都是阻塞在sigsuspend()函数调用上.Worker进程屏蔽了所有的外部信号,那么Master进程就通过套接字和worker进程通信,worker进程修改全局变量,使得worker进程接受

nginx源码分析--模块分类

ngx-modules Nginx 主要的模块大致可以分为四类: handler – 协同完成客户端请求的处理.产生响应数据.比如模块, ngx_http_rewrite_module, ngx_http_log_module, ngx_http_static_module. filter – 对 handler 产生的响应数据做各种过滤处理.比如模块, ngx_http_not_modified_filter_module, ngx_http_header_filter_module. ups

nginx源码分析--ngx_http_optimize_servers()函数

这个函数做了连部分工作:1)以端口为入口点 将有用的信息存放到hash表内 2)调用ngx_http_init_listening()函数 对端口进行监听 1. 在ngx_http_core_main_conf_t结构体中有一个字段为ports,是一个数组,数组内存放的全是ngx_http_conf_port_t:对于每一个端口信息(ngx_http_conf_port_t),调用 ngx_http_server_names函数,同时也调用ngx_http_init_listening函数,这里

nginx源码分析--从源码看nginx框架总结

nginx源码总结: 1)代码中没有特别绕特别别扭的编码实现,从变量的定义调用函数的实现封装,都非常恰当,比如从函数命名或者变量命名就可以看出来定义的大体意义,函数的基本功能,再好的架构实现在编码习惯差的人实现也会黯然失色,如果透彻理解代码的实现,领悟架构的设计初衷,觉得每块代码就想经过耐心雕琢一样,不仅仅实现了基本的功能给你,为其他人阅读也会提供很好的支持.细致恰当的命名规则就可以看出作者的功力. 2)更好更高的软件性能体现在架构设计上,好的架构会让软件更加稳定.容易维护.便于扩展.从核心模块

nginx源码分析--nginx外部信号 命令参数

nginx命令行参数 不像许多其他软件系统,Nginx 仅有几个命令行参数,完全通过配置文件来配置 -c </path/to/config> 为 Nginx 指定一个配置文件,来代替缺省的. -t 不运行,而仅仅测试配置文件.nginx 将检查配置文件的语法的正确性,并尝试打开配置文件中所引用到的文件. -v 显示 nginx 的版本. -V 显示 nginx 的版本,编译器版本和配置参数. nginx控制信号 可以使用信号系统来控制主进程.默认,nginx 将其主进程的 pid 写入到 /u

nginx源码分析--监听套接字的创建 套接字的监听 HTTP请求创建连接

作为一个web服务器,那么肯定是有监听套接字的,这个监听套接字是用于接收HTTP请求的,这个监听套接字的创建是根据配置文件的内容来创建的,在nginx.conf文件中有多少个地址就需要创建多少个监听套接字.这里不说各个结构体的构造 只说大体情况! 1).首先在main函数中调用了ngx_init_cycle()函数,在这个函数的最后调用了ngx_open_listening_sockets函数,这个函数负责将创建的监听套接字进行套接字选项的设置(比如非阻塞.接受发送的缓冲区.绑定.监听处理) 2