Nginxのメモリ管理

WBOY
リリース: 2016-07-30 13:31:32
オリジナル
1330 人が閲覧しました

1. ソースコードの場所

ヘッダーファイル: http://trac.nginx.org/nginx/browser/nginx/src/core/ngx_palloc.h

ソースファイル: http://trac.nginx.org/ nginx/browser/nginx/src/core/ngx_palloc.c

2. データ構造の定義

まず、nginx メモリ プールのいくつかの主要なデータ構造について学びましょう:

ngx_pool_data_t (メモリ プール データ ブロック構造)

リー
  • last: これは、現在のメモリ プールに割り当てられた最後のアドレスを保存する unsigned char 型のポインタです。つまり、次の割り当てはここから始まります。
  • end: メモリ プールの終了位置;
  • next: メモリ プールには多数のメモリ ブロックがあり、これらのメモリ ブロックはこのポインタを通じてリンク リストに接続され、next はメモリの次のブロックを指します。
  • failed: メモリ プール割り当ての失敗の数。

ngx_pool_s (メモリプールヘッダ構造)

<span> 1:</span><span>typedef</span> <span>struct</span> {<precourier new width:950px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 2:</span>     u_char               *last;        <precourier new width:950px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 3:</span>     u_char               *end;<precourier new width:950px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 4:</span>     ngx_pool_t           *next;<precourier new width:950px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 5:</span>     ngx_uint_t            failed;<precourier new width:950px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 6:</span> } ngx_pool_data_t;<precourier new width:950px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 1:</span><span>struct</span> ngx_pool_s {<ul> <li>d: メモリプールのデータブロック </li> <li>max: メモリプールのデータブロックの最大値。 </li>current: 現在を指しますメモリプール; <li>chain: このポインタは ngx_chain_t 構造体にアタッチされます; <li>large: 割り当てられたスペースが最大を超えたときに使用されるメモリのリンクされたリストの大きなブロック。 log: ログ情報 <li> <li> は、ngx_pool_data_t と ngx_pool_t で構成されます。 構成された nginx メモリ プールの構造を以下の図に示します。 </li> <li> </ul><p> 3. 関連関数の紹介 </p><p><img src="http://image.codes51.com/Article/image/20150814/20150814194335_5243.jpg" style="max-width:90%" style="max-width:90%" alt="Nginxのメモリ管理" > メモリ プール方式を分析する前に、次のことを行う必要があります。いくつかのメインメモリ関連関数を導入します: </p><p>ngx_alloc: (just对malloc进行了简单的封装)</p><blockquote> <p><precourier new width:950px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 2:</span>     ngx_pool_data_t       d;<precourier new width:950px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 3:</span>     size_t                max;<precourier new width:950px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 4:</span>     ngx_pool_t           *current;<precourier new width:950px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 5:</span>     ngx_chain_t          *chain;<precourier new width:950px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 6:</span>     ngx_pool_large_t     *large;<precourier new width:950px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 7:</span>     ngx_pool_cleanup_t   *cleanup;<precourier new width:950px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 8:</span>     ngx_log_t            *log;<precourier new width:950px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 9:</span> };<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 1:</span><span>void</span> *<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 2:</span> ngx_alloc(size_t size, ngx_log_t *log)<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 3:</span> {<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 4:</span>     <span>void</span>  *p;</precourier></precourier></precourier></precourier></precourier></precourier></precourier></precourier></precourier></precourier></precourier></precourier></p> <p></p> <p> <divcourier new width:870px color:black padding:0px direction:ltr line-height:12pt background-color:rgb>ngx_calloc:(调用malloc并初期化0)</divcourier></p> </blockquote><blockquote><p><precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 5:</span>  <precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 6:</span>     p = malloc(size);<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 7:</span>     <span>if</span> (p == NULL) {<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 8:</span>         ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 9:</span>                       <span>"malloc(%uz) failed"</span>, size);<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 10:</span>     }<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 11:</span>  <precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 12:</span>     ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, <span>"malloc: %p:%uz"</span>, p, size);<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 13:</span>  <precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 14:</span>     <span>return</span> p;<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 15:</span> }<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 1:</span><span>void</span> *<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 2:</span> ngx_calloc(size_t size, ngx_log_t *log)<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 3:</span> {<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 4:</span>     <span>void</span>  *p;<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 5:</span>  </precourier></precourier></precourier></precourier></precourier></precourier></precourier></precourier></precourier></precourier></precourier></precourier></precourier></precourier></precourier></precourier></p></blockquote><blockquote> <p><divcourier new width:870px color:black padding:0px direction:ltr line-height:12pt background-color:rgb>ngx_memzero:</divcourier></p> </blockquote><precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 6:</span>     p = ngx_alloc(size, log);<blockquote> <p></p> <p><divcourier new width:870px color:black padding:0px direction:ltr line-height:12pt background-color:rgb>ngx_free:</divcourier></p> </blockquote><precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 7:</span>  <blockquote> <p></p> <p><divcourier new width:870px color:black padding:0px direction:ltr line-height:12pt background-color:rgb>ngx_memalign:</divcourier></p> </blockquote><blockquote>rrereerreerreerreerreerreerreerreerreerreerreerreerreerreerreerreerreerreerreerreerreeほとんどの場合、コンパイラと C ライブラリがアラインメントの問題を透過的に処理します。 nginx はマクロ NGX_HAVE_POSIX_MEMALIGN によって制御されており、<p>posix_memalign( )</p> を正常に呼び出すと、<p>size<divcourier new width:870px color:black padding:0px direction:ltr line-height:12pt background-color:rgb> バイトの動的メモリが返され、このメモリのアドレスは </divcourier></p>alignment の倍数になります。パラメーター </blockquote>alignment は 2 のべき乗、または <limicrosoft yahei font-size:15px line-height:35px>void<blockquote> ポインターのサイズの倍数でなければなりません。返されたメモリブロックのアドレスは<em>memptr</em>に置かれ、関数の戻り値は<em>0</em>.<em></em><em></em> <em></em>4. メモリプールの基本動作<em><ul><li>メモリプールの主な外部メソッドは次のとおりです: </li></ul> <limicrosoft yahei font-size:15px line-height:35px><table cellspacing="0" cellpadding="2" border="1"><tbody> <tr> <td valign="top" width="118">メモリプールを作成する</td> <td valign="top" width="768">ngx_pool_t * ngx_create_pool(size_t size, ngx_log_t *log);</td> </tr> <tr> <td valign="top" width="118">メモリプールを破棄する</td> <td valign="top" width="768">vo ID ngx_destroy_pool(ngx_pool_t *pool ; (調整されていません)</td> </tr>void * ngx_pnalloc(ngx_pool_t *pool, size_t size);<tr> <td valign="top" width="118"></td> <td valign="top" width="768">メモリクリア</td> </tr>ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);<tr> <td valign="top" width="118"></td> <td valign="top" width="768"><limicrosoft yahei font-size:15px line-height:35px><limicrosoft yahei font-size:15px line-height:35px><p>4.1 创建内存池ngx_create_pool</p> <blockquote><p>ngx_create_pool用于创建一个内存池,我们创建时,传入我们的需要的初始大小:</p></blockquote> <limicrosoft yahei font-size:15px line-height:35px><blockquote><p><divcourier new width:870px color:black padding:0px direction:ltr line-height:12pt background-color:rgb><precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 1:</span> ngx_pool_t *<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 2:</span> ngx_create_pool(size_t size, ngx_log_t *log)<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 3:</span> {<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 4:</span>     ngx_pool_t  *p;<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 5:</span>     <precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 6:</span>     <span>//以16(NGX_POOL_ALIGNMENT)字节对齐分配size内存</span><precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 7:</span>     p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 8:</span>     <span>if</span> (p == NULL) {<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 9:</span>         <span>return</span> NULL;<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 10:</span>     }<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 11:</span>  <precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 12:</span>     <span>//初始状态:last指向ngx_pool_t结构体之后数据取起始位置</span><precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 13:</span>     p->d.last = (u_char *) p + <span>sizeof</span>(ngx_pool_t);<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 14:</span>     <span>//end指向分配的整个size大小的内存的末尾</span><precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 15:</span>     p->d.end = (u_char *) p + size;<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 16:</span>     <precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 17:</span>     p->d.next = NULL;<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 18:</span>     p->d.failed = 0;<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 19:</span>  <precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 20:</span>     size = size - <span>sizeof</span>(ngx_pool_t);<precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 21:</span>     <span>//#define NGX_MAX_ALLOC_FROM_POOL  (ngx_pagesize - 1),内存池最大不超过4095,x86中页的大小为4K</span><precourier new width:870px padding:0px direction:ltr margin:0em line-height:12pt background-color:rgb><span> 22:</span>     p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;
ログイン後にコピー
<span> 23:</span>
ログイン後にコピー
24: p->current = p; 25:     p->chain = NULL; 26:     p->large = NULL; 27:     p->cleanup = NULL; 28:     p->log = log; 29:  30:     return p; 31: }

nginx对内存的管理分为大内存与小内存,当某一个申请的内存大于某一个值时,就需要从大内存中分配空间,否则从小内存中分配空间。

nginx中的内存池是在创建的时候就设定好了大小,在以后分配小块内存的时候,如果内存不够,则是重新创建一块内存串到内存池中,而不是将原有的内存池进行扩张。当要分配大块内存是,则是在内存池外面再分配空间进行管理的,称为大块内存池。

 

4.2 内存申请 ngx_palloc

1:void * 2: ngx_palloc(ngx_pool_t *pool, size_t size) 3: { 4:     u_char      *m; 5:     ngx_pool_t  *p; 6:  7:     //如果申请的内存大小小于内存池的max值 8:     if (size <= pool->max) { 9:  10:         p = pool->current; 11:  12:         do { 13:             //对内存地址进行对齐处理 14:             m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT); 15:  16:             //如果当前内存块够分配内存,则直接分配 17:             if ((size_t) (p->d.end - m) >= size) 18:             { 19:                 p->d.last = m + size; 20:  21:                 return m; 22:             } 23:             24:             //如果当前内存块有效容量不够分配,则移动到下一个内存块进行分配 25:             p = p->d.next; 26:  27:         } while (p); 28:  29:         //当前所有内存块都没有空闲了,开辟一块新的内存,如下2详细解释 30:         return ngx_palloc_block(pool, size); 31:     } 32:  33:     //分配大块内存 34:     return ngx_palloc_large(pool, size); 35: }

需要说明的几点:

1、ngx_align_ptr,这是一个用来内存地址取整的宏,非常精巧,一句话就搞定了。作用不言而喻,取整可以降低CPU读取内存的次数,提高性能。因为这里并没有真正意义调用malloc等函数申请内存,而是移动指针标记而已,所以内存对齐的活,C编译器帮不了你了,得自己动手。

1: #define ngx_align_ptr(p, a)                                                   \ 2:      (u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))

2、开辟一个新的内存块 ngx_palloc_block(ngx_pool_t *pool, size_t size)

这个函数是用来分配新的内存块,为pool内存池开辟一个新的内存块,并申请使用size大小的内存;

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; 7:  8:     //计算内存池第一个内存块的大小 9:     psize = (size_t) (pool->d.end - (u_char *) pool); 10:  11:     //分配和第一个内存块同样大小的内存块 12:     m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log); 13:     if (m == NULL) { 14:         return NULL; 15:     } 16:  17:     new = (ngx_pool_t *) m; 18:  19:     //设置新内存块的end 20:     new->d.end = m + psize; 21:     new->d.next = NULL; 22:     new->d.failed = 0; 23:  24:     //将指针m移动到d后面的一个位置,作为起始位置 25:     m += sizeof(ngx_pool_data_t); 26:     //对m指针按4字节对齐处理 27:     m = ngx_align_ptr(m, NGX_ALIGNMENT); 28:     //设置新内存块的last,即申请使用size大小的内存 29:     new->d.last = m + size; 30:  31:     //这里的循环用来找最后一个链表节点,这里failed用来控制循环的长度,如果分配失败次数达到5次,就忽略,不需要每次都从头找起 32:     for (p = pool->current; p->d.next; p = p->d.next) { 33:         if (p->d.failed++ > 4) { 34:             pool->current = p->d.next; 35:         } 36:     } 37:  38:     p->d.next = new; 39:  40:     return m; 41: }

3、分配大块内存 ngx_palloc_large(ngx_pool_t *pool, size_t size)

在ngx_palloc中首先会判断申请的内存大小是否超过内存块的最大限值,如果超过,则直接调用ngx_palloc_large,进入大内存块的分配流程;

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:     // 直接在系统堆中分配一块大小为size的空间 9:     p = ngx_alloc(size, pool->log); 10:     if (p == NULL) { 11:         return NULL; 12:     } 13:  14:     n = 0; 15:  16:     // 查找到一个空的large区,如果有,则将刚才分配的空间交由它管理  17:     for (large = pool->large; large; large = large->next) { 18:         if (large->alloc == NULL) { 19:             large->alloc = p; 20:             return p; 21:         } 22:         //为了提高效率, 如果在三次内没有找到空的large结构体,则创建一个 23:         if (n++ > 3) { 24:             break; 25:         } 26:     } 27:  28:  29:     large = ngx_palloc(pool, sizeof(ngx_pool_large_t)); 30:     if (large == NULL) { 31:         ngx_free(p); 32:         return NULL; 33:     } 34:     35:     //将large链接到内存池 36:     large->alloc = p; 37:     large->next = pool->large; 38:     pool->large = large; 39:  40:     return p; 41: }

整个内存池分配如下图:

Nginxのメモリ管理

  • 4.3 内存池重置 ngx_reset_pool

1:void 2: ngx_reset_pool(ngx_pool_t *pool) 3: { 4:     ngx_pool_t        *p; 5:     ngx_pool_large_t  *l; 6:     7:     //释放大块内存 8:     for (l = pool->large; l; l = l->next) { 9:         if (l->alloc) { 10:             ngx_free(l->alloc); 11:         } 12:     } 13:     14:     // 重置所有小块内存区 15:     for (p = pool; p; p = p->d.next) { 16:         p->d.last = (u_char *) p + sizeof(ngx_pool_t); 17:         p->d.failed = 0; 18:     } 19:  20:     pool->current = pool; 21:     pool->chain = NULL; 22:     pool->large = NULL; 23: }

4.4 内存池释放 ngx_pfree

1: ngx_int_t 2: ngx_pfree(ngx_pool_t *pool, void *p) 3: { 4:     ngx_pool_large_t  *l; 5:  6:     //只检查是否是大内存块,如果是大内存块则释放 7:     for (l = pool->large; l; l = l->next) { 8:         if (p == l->alloc) { 9:             ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, 10:                            "free: %p", l->alloc); 11:             ngx_free(l->alloc); 12:             l->alloc = NULL; 13:  14:             return NGX_OK; 15:         } 16:     } 17: 

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート