メモリプールの概要メモリ プールは、メモリが実際に使用される前にバックアップとして事前に割り当てられる、通常は同じサイズの一定数のメモリ ブロックです。新しいメモリ需要が発生すると、メモリ ブロックの一部がメモリ プールから割り当てられます。メモリ ブロックが十分でない場合は、新しいメモリが要求されます。 メモリ プールの利点には、システムへのメモリの適用と解放にかかる時間のオーバーヘッドの削減、頻繁なメモリ割り当てによって発生するメモリの断片化の問題の解決、プログラムのパフォーマンスの向上、コード作成時のプログラマーのメモリへの注意の軽減などがあります。 現在、一般的なメモリ プールの実装としては、STL のメモリ割り当て領域、boost の Nginx は、利便性のために、ngx_str_t、ngx_array_t、ngx_pool_t などの多くの便利なデータ構造をカプセル化しています。メモリ プールに関しては、nginx の設計は非常に洗練されており、学習する価値があります。この記事では、nginx メモリ プールのソース コードの紹介に焦点を当て、実際のコード例を使用してさらに説明します。 1. nginxデータ構造// SGI STL の小メモリブロックと大メモリブロックの分割点: 128B // nginx (HTTP サーバーのすべてのモジュールにメモリを割り当てます) メモリの小さなブロックと大きなブロックの分割点: 4096B # NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1) を定義します // メモリプールのデフォルトサイズ #define NGX_DEFAULT_POOL_SIZE (16 * 1024) // メモリプールのバイトアラインメント、SGI STL は 8B #定義 NGX_POOL_ALIGNMENT 16 # NGX_MIN_POOL_SIZE を ngx_align((sizeof(ngx_pool_t) + 2 * sizeof(ngx_pool_large_t)), で定義します。\ NGX_プール_アラインメント // 割り当てられたメモリを 16 の整数倍に調整します #define ngx_align(d, a) (((d) + (a - 1)) & ~(a - 1)) typedef 構造体 ngx_pool_s ngx_pool_t; typedef構造体{ 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; // スペースを割り当てるために使用できるメモリ ブロックの開始アドレスを指します (失敗 < 4) ngx_chain_t *chain; // すべてのメモリ プール ブロックを接続します ngx_pool_large_t *large; // 大きなメモリ ブロックのエントリ ポインター ngx_pool_cleanup_t *cleanup; // メモリ プール ブロックのクリーンアップ操作。ユーザーは、メモリ プール ブロックを解放する前にクリーンアップ操作を実行するコールバック関数を設定できます ngx_log_t *log; // ログ }; 2. nginxはOSからスペースngx_create_poolを申請します// サイズに応じてメモリを開放する ngx_pool_t * ngx_create_pool(size_t size, ngx_log_t *log){ ngx_pool_t *p; // システム プラットフォームによって定義されたマクロとユーザーが実行したサイズに応じて、さまざまなプラットフォームの API を呼び出してメモリ プールを開きます。p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log); (p == NULL)の場合{ NULL を返します。 } 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); // 使用可能なスペース = 合計スペース - ヘッダー情報 // 指定されたサイズが 1 ページより大きい場合は 1 ページを使用し、それ以外の場合は指定されたサイズを使用します // max = min(size, 4096)、max は開始情報を除いたメモリ ブロックのサイズを参照します p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL; p->current = p; // スペースを割り当てるために使用できるメモリ ブロックの開始アドレスを指します p->chain = NULL; p->large = NULL; // 小さなメモリ ブロックはメモリ ブロックに直接割り当てられ、大きなメモリ ブロックは large が指すメモリに割り当てられます。p->cleanup = NULL; p->log = ログ; p を返します。 } 3. nginxはメモリプールからスペースを申請します空所 * ngx_palloc(ngx_pool_t *プール、size_t サイズ) { #if !(NGX_DEBUG_PALLOC) (サイズ <= プール-> 最大) の場合 { // 現在割り当てられているスペースは最大値より小さいため、メモリ割り当てが小さいです。 return ngx_palloc_small(pool, size, 1); // メモリの配置を考慮します} #終了 ngx_palloc_large(プール、サイズ) を返します。 } 空所 * ngx_pnalloc(ngx_pool_t *プール、size_t サイズ) { #if !(NGX_DEBUG_PALLOC) (サイズ <= プール-> 最大) の場合 { return ngx_palloc_small(pool, size, 0); // メモリのアラインメントは考慮されません} #終了 ngx_palloc_large(プール、サイズ) を返します。 } void* ngx_pcalloc(ngx_pool_t *プール、size_t サイズ){ void *p; p = ngx_palloc(pool, size); // メモリアライメントを考慮する if (p) { ngx_memzero(p, size); // メモリを0に初期化する } p を返します。 }
静的 ngx_inline void * ngx_palloc_small(ngx_pool_t *プール、size_t サイズ、ngx_uint_t 配置) { u_char *m; ngx_pool_t *p; // 最初のメモリ ブロックの現在のポインタが指すメモリ プールから割り当てます。p = pool->current; する { m = p->d.last; // mは割り当て可能なメモリの開始アドレスを指します if (align) { // m を NGX_ALIGNMENT の整数倍に調整します。m = ngx_align_ptr(m, NGX_ALIGNMENT); } //メモリプールからメモリを割り当てるためのコアコードif ((size_t) (p->d.end - m) >= size) { // 割り当て可能なスペース >= 要求されたスペースの場合 // d.last ポインターをオフセットし、空きスペースの最初のアドレスを記録します p->d.last = m + size; m を返します。 } // 現在のメモリブロックの空き領域は割り当てるには不十分です。次のメモリブロックがある場合は、次のメモリブロックに進みます。 // ない場合は、p は NULL に設定され、終了すると同時に p = p->d.next; } ながら (p); ngx_palloc_block(プール、サイズ) を返します。 } 現在のメモリ プールには割り当てるのに十分なブロックがあります: 現在のメモリ プールには割り当てるのに十分なブロックがありません:
静的void * ngx_palloc_block(ngx_pool_t *プール、size_tサイズ){ u_char *m; size_t pサイズ; ngx_pool_t *p、*新しい; // 前のメモリ ブロックと同じサイズのメモリ ブロックを開きます。psize = (size_t) (pool->d.end - (u_char *) pool); // psize を NGX_POOL_ALIGNMENT の整数倍に揃えた後、OS からスペースを申請します m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log); (m == NULL)の場合{ NULL を返します。 } new = (ngx_pool_t *) m; // 新しく割り当てられたメモリ ブロックの開始アドレスを指します new->d.end = m + psize; // 新しく割り当てられたメモリ ブロックの終了アドレスを指します new->d.next = NULL; // 次のメモリ ブロックのアドレスは NULL です new->d.failed = 0; // 現在のメモリ ブロックがスペースの割り当てに失敗した回数 // ヘッダー情報の末尾を指しますが、max、current、chain などは最初のメモリ ブロックにのみ存在します m += sizeof(ngx_pool_data_t); m = ngx_align_ptr(m, NGX_ALIGNMENT); new->d.last = m + size; // last は現在のブロックの空き領域の開始アドレスを指します // 毎回 pool->current からスペースが割り当てられるため // 実行がここに到達すると、新しいメモリ ブロックを除いて、他のすべてのメモリ ブロックの割り当てに失敗します (p = pool->current; p->d.next != NULL; p = p->d.next) { // すべてのメモリ ブロックに対して、メモリ ブロックの割り当て失敗数が 4 を超えるまで failed++ が追加されます。 // これは、メモリ ブロックの残りのスペースが非常に少なく、これ以上スペースを割り当てることができないことを意味します。 // 次に、現在のポインタを変更し、次回は現在のポインタからスペースの割り当てを開始します。再度割り当てるときに、以前のメモリ ブロックをトラバースする必要はありません。 if (p->d.failed++ > 4) { プール->current = p->d.next; } } p->d.next = new; // 割り当て可能なスペースの最初のメモリ ブロックと新しく開いたメモリ ブロックを接続します。 return m; } 4. 大きなメモリブロックの割り当てと解放typedef 構造体 ngx_pool_large_s ngx_pool_large_t; 構造体 ngx_pool_large_s { ngx_pool_large_t *next; // 次の大きなメモリ ブロックの開始アドレス void *alloc; // 大きなメモリ ブロックの開始アドレス }; 静的void * ngx_palloc_large(ngx_pool_t *プール、size_tサイズ){ void *p; ngx_uint_t n; ngx_pool_large_t *大きい; // malloc を呼び出す p = ngx_alloc(サイズ、プール->ログ); (p == NULL)の場合{ NULL を返します。 } 0 の場合 // for ループは、大きなメモリ情報ブロックを格納するリンクリストを走査します。for (large = pool->large; large; large = large->next) { (large->alloc == NULL)の場合{ // 大きなメモリブロックが ngx_pfree の場合、alloc は NULL になります // リンクリストを走査し、大きなメモリブロックの最初のアドレスが空の場合は、現在の malloc メモリアドレスを alloc に書き込みます。 大きい->割り当て = p; p を返します。 } // 4 回トラバースした後、解放された大きなメモリ ブロックの対応する情報が見つからない場合 // 効率を向上させるために、大きなメモリ ブロックの情報を保存するために、小さなメモリ ブロックのスペースを直接申請します if (n++ > 3) { 壊す; } } // ポインターオフセットを介して小さなメモリプールに大規模なメモリブロック *next と *alloc を格納するためのスペースを割り当てます。large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1); 大きい == NULL の場合 { // 小さなメモリブロック上のストレージ *next と *alloc スペースの割り当てが失敗した場合、大きなメモリブロックは記録できません // 大きなメモリブロックを解放します p ngx_free(p); NULL を返します。 } large->alloc = p; // alloc は大きなメモリ ブロックの最初のアドレスを指します。large->next = pool->large; // これらの 2 つの文は、ヘッド挿入方式を使用して、large をヘッド ノードとしてリンク リストに新しいメモリ ブロックのレコード情報を格納します。pool->large = large; p を返します。 } 大きなメモリブロックの解放 // p が指す大きなメモリブロックを解放する ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p){ ngx_pool_large_t *l; (l = pool->large; l; l = l->next) { // 大きなメモリブロックを格納するリンクリストを走査し、pに対応する大きなメモリブロックを見つける if (p == l->alloc) { ngx_log_debug1(NGX_LOG_DEBUG_ALLOC、プール->ログ、0、 "解放: %p", l->alloc); // 大きなメモリブロックを解放しますが、情報が格納されているメモリ領域は解放しません ngx_free(l->alloc); // 解放 l->alloc = NULL; // alloc は NULL に設定され、NGX_OK を返します。 } } NGX_DECLINED を返します。 } 5. 解放されない小さなメモリブロックについて最後のキーと終了キーは空き領域を示すために使用されます。 使用された領域を合理的にメモリプールに戻すことは不可能ですが、メモリプールはリセットされます。また、大きなメモリブロックを指すヘッダー情報とクリーンアップ関数cleanupも保存します。 nginxの効率性を考慮して、小さなメモリブロックが効率的に割り当てられ、同時にメモリがリサイクルされない void ngx_reset_pool(ngx_pool_t *プール){ ngx_pool_t *p; ngx_pool_large_t *l; // 小さいメモリをリセットする必要があり、大きいメモリの制御情報は小さいメモリに保存されるため、 // 最初に大きいメモリを解放し、次に小さいメモリをリセットする必要があります for (l = pool->large; l; l = l->next) { もし(l->割り当て){ ngx_free(l->alloc); } } // 小さなメモリブロックのリンクリストを走査し、last、failed、current、chain、large などの管理情報をリセットします。 for (p = pool; p; p = p->d.next) { // 最初のメモリ ブロックのみに ngx_pool_data_t 以外の管理情報があるため、他のメモリ ブロックには ngx_pool_data_t の情報のみが含まれます // エラーは発生しませんが、スペースが無駄になります p->d.last = (u_char *) p + sizeof(ngx_pool_t); p->d.失敗 = 0; } // current はメモリを割り当てるために使用できるメモリ ブロックを指します。pool->current = pool; プール->チェーン = NULL; プール->large = NULL; } Nginx は本質的に http サーバーです。通常は短いリンクを処理し、間接的にサービスを提供します。メモリをあまり必要としないため、メモリをリサイクルせず、リセットできます。 クライアントがリクエストを開始すると、nginx サーバーはリクエストを受信した後、応答を返します。キープアライブ時間内にクライアントから別のリクエストを受信しない場合、nginx サーバーは積極的に切断し、メモリ プールをリセットします。次にクライアント要求が届いたときに、メモリ プールを再利用できます。 長時間の接続を処理する場合、クライアントがオンラインである限り、システム リソースが使い果たされるまでサーバー リソースを解放することはできません。長いリンクでは通常、メモリの割り当てと解放に SGI STL メモリ プールが使用されますが、この方法はスペースの割り当てと再利用において nginx よりも効率が低くなります。 6. メモリプールを破壊してクリアする次のような状況を想定します。 // メモリアラインメントが4Bであると仮定 typedef構造体{ 文字* p; 文字データ[508]; }stデータ; ngx_pool_t *pool = ngx_create_pool(512, log); // 合計 512B のスペースを持つ nginx メモリ ブロックを作成します。 stData* data_ptr = ngx_alloc(512); // 実際に使用可能なメモリ サイズは 512-sizeof(ngx_pool_t) であるため、大きなメモリ割り当てに属します。 data_ptr->p = malloc(10); // p は外部ヒープ メモリを指し、C++ オブジェクトでの外部リソースの使用に似ています。 大きなメモリブロックを再利用する場合、ngx_freeを呼び出すとメモリリークが発生します。 上記のメモリリーク問題は、コールバック関数(関数ポインタを通じて実装)を通じてメモリを解放することで解決できます。 typedef void (*ngx_pool_cleanup_pt)(void *data); typedef 構造体 ngx_pool_cleanup_s ngx_pool_cleanup_t; // 次の構造体は ngx_pool_s.cleanup によって指し示され、これもメモリプールに格納される小さなメモリブロックです。 struct ngx_pool_cleanup_s { ngx_pool_cleanup_pt ハンドラ; void *data; // 解放する必要があるリソースを指します ngx_pool_cleanup_t *next; // リソースを解放する関数はリンク リストに配置され、next はこのリンク リストを指すために使用されます}; nginx が提供する関数インターフェース: // p はメモリプールのエントリアドレスを表し、size は p->cleanup->data ポインタのサイズを表します // p->cleanup はクリーンアップ関数情報を含む構造体を指します // ngx_pool_cleanup_add はクリーンアップ関数情報を含む構造体へのポインタを返します ngx_pool_cleanup_t* ngx_pool_cleanup_add(ngx_pool_t *p, size_t size){ ngx_pool_cleanup_t *c; // クリーンアップ関数の構造は、実際にはメモリプールに格納されている小さなメモリブロックです。c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t)); c == NULLの場合{ NULL を返します。 } if (サイズ) { // サイズのスペースを適用します for c->data c->data = ngx_palloc(p, size); c->data == NULLの場合{ NULL を返します。 } } それ以外 { c->データ = NULL; } c->ハンドラ = NULL; // ヘッド挿入メソッドを使用して、pool->cleanup の後に現在の構造を文字列化します。c->next = p->cleanup; p->クリーンアップ = c; ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "クリーンアップを追加: %p", c); c を返します。 } 方向: void リリース(void* p){ 無料(p); } ngx_pool_cleanup_t* clean_ptr = ngx_clean_cleanup_add(pool, sizeof(char*)); clean_ptr->handler = &release; // ユーザーはメモリプールを破棄する前に呼び出される関数を設定します clean_ptr->data = data_ptr->p; // ユーザーはメモリプールを破棄する前に解放するメモリのアドレスを設定します ngx_destroy_pool(pool); // ユーザーはメモリプールを破棄します 7. メモリプールインターフェース関数をコンパイルしてテストするvoid ngx_destroy_pool(ngx_pool_t *プール) { ngx_pool_t *p、*n; ngx_pool_large_t *l; ngx_pool_cleanup_t *c; // クリーンアップリスト(解放前に呼び出す必要がある関数として保存されている)を走査して外部リソースを解放します for (c = pool->cleanup; c; c = c->next) { if (c->ハンドラ) { ngx_log_debug1(NGX_LOG_DEBUG_ALLOC、プール->ログ、0、 "クリーンアップを実行: %p", c); c->ハンドラ(c->データ); } } // 大きなメモリブロックを解放する for (l = pool->large; l; l = l->next) { もし(l->割り当て){ ngx_free(l->alloc); } } // 小さなメモリプールを解放します for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) { ngx_free(p); (n == NULL)の場合{ 壊す; } } } Makefile は次のとおりです。 makeコマンドを実行してMakefileを使用してソースコードをコンパイルし、対応するディレクトリに #include <ngx_config.h> #include <nginx.h> #include <ngx_core.h> #include <ngx_palloc.h> #include <stdio.h> #include <stdlib.h> #include <文字列.h> void ngx_log_error_core(ngx_uint_t レベル、ngx_log_t *ログ、ngx_err_t エラー、 定数char *fmt, ...){ } typedef 構造体 Data stData; 構造体データ{ char *ptr; ファイル *pfile; }; void関数1(char *p){ printf("ptrメモリを解放してください!\n"); 無料(p); } void func2(FILE *pf){ printf("ファイルを閉じます!\n"); fclose(pf); } void main(){ // 最大 = 512 - sizeof(ngx_pool_t) // 合計 512 バイトのスペースを持つ nginx メモリ ブロックを作成します。 ngx_pool_t *pool = ngx_create_pool(512, NULL); if(プール == NULL){ printf("ngx_create_pool が失敗しました..."); 戻る; } // 小さなメモリから割り当てられた void *p1 pool = ngx_palloc(pool, 128); p1 == NULLの場合{ printf("ngx_palloc 128 バイトが失敗しました..."); 戻る; } // stData *p2 は大容量メモリから割り当てられます pool = ngx_palloc(pool, 512); p2 == NULLの場合{ printf("ngx_palloc 512 バイトが失敗しました..."); 戻る; } // 外部ヒープメモリを占有 p2->ptr = malloc(12); strcpy(p2->ptr, "こんにちは世界"); // ファイル記述子 p2->pfile = fopen("data.txt", "w"); ngx_pool_cleanup_t *c1 = ngx_pool_cleanup_add(プール、sizeof(char*)); c1->handler = func1; // コールバック関数を設定 c1->data = p2->ptr; // リソースアドレスを設定 ngx_pool_cleanup_t *c2 = ngx_pool_cleanup_add(pool, sizeof(FILE*)); c2->ハンドラ = func2; c2->データ = p2->pファイル; // 1. 事前に設定されたすべてのクリーンアップ関数を呼び出す 2. 大きなメモリ ブロックを解放する 3. 小さなメモリ プール内のすべてのメモリを解放する ngx_destroy_pool(pool); 戻る; } 作成されたクリーンアップ ブロックは、 関連するテストコードは https://github.com/BugMaker-shen/nginx_sgistl_pool にプッシュされています。 nginx メモリ プールのソース コード分析に関するこの記事はこれで終わりです。より関連性の高い nginx メモリ プールのコンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。 以下もご興味があるかもしれません:
|
>>: FirefoxでCookieとお気に入りをインポートおよびエクスポートする方法
1. テーブルを作成する<br /> まず、2 つのテーブル (users テーブルと...
この例の Web カラー ピッカー機能は、ページ効果を実現するために CSS3 を使用します。つまり...
Microsoft IE 5.0 がリリースされる前は、Web プログラミングにおける最大の課題は、...
/******************** * カーネルにおけるリンクリストの応用********...
効果は以下のとおりです。参考プログラム: <!DOCTYPE html> <htm...
この記事は主に、MySQLを再インストールする際のクリーンでないアンインストールのさまざまな問題をま...
まとめプロジェクトの説明形式<img src="..."> H2+ ...
1. cmakeをインストールする1. cmakeの圧縮パッケージを解凍する [root@mysql...
MySQLとElasticsearch間のデータ非対称性問題の解決策jdbc-input-plugi...
◆お気に入りに追加例示するクリックすると、ブラウザのお気に入りメニューにウェブサイトが追加されます...
HTML のセマンティクスはありふれた問題のようです。Google で検索すると、セマンティクスに関...
メソッドが定義されているクラスに応じて、現在のクラスへの静的参照を取得するには、self:: または...
Vue.js を使用して、クリックしてズームできる 9 グリッドの画像表示モジュールを作成しました。...
これはかなり前に書かれた記事です。今となっては、その中の考え方は学ぶ価値があるように思えます。jb5...
Docker の作成Docker Compose は、管理対象コンテナをプロジェクト、サービス、コン...