Versi stabil terkini nginx1.20.2.
Untuk memperuntukkan memori dengan cekap dan cepat, dan mengurangkan pemecahan memori, nginx melaksanakan komponen kumpulan memori asasnya sendiri.
Fail pelaksanaan utamangx_palloc.h, ngx_palloc.c
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; };
Ahli pertama kumpulan memori ialah struktur:
Gunakan struktur ngx_pool_data_t untuk mewakili maklumat kumpulan memori semasa.
terakhir: alamat yang akan diperuntukkan kali seterusnya
tamat: alamat akhir kumpulan memori
seterusnya: senarai terpaut kolam memori, menyambungkan berbilang kumpulan memori
maks
keseluruhan kolam memori Saiz maksimum
arus
menunjukkan bermula dari kolam memori semasa untuk mencari memori yang tersedia
rantai
digunakan oleh penimbal, yang tidak terlibat di sini apabila
besar
Apabila memori yang diperlukan lebih besar daripada saiz maksimum kumpulan memori, ia perlu diperuntukkan terus melalui malloc dan kemudian disusun ke dalam senarai terpaut
pembersihan
Panggilan balik dipautkan senarai untuk kerja pembersihan
log
Pemegang log
Apabila memori yang akan diperuntukkan lebih besar daripada saiz maksimum kumpulan memori, kolam memori tidak dapat memenuhi peruntukan, jadi ia diperuntukkan terus daripada sistem dan kemudian membentuk senarai terpaut untuk penyelenggaraan.
typedef struct ngx_pool_large_s ngx_pool_large_t; struct ngx_pool_large_s { ngx_pool_large_t *next; void *alloc; };
Terdapat senarai tugasan panggil balik terpaut Apabila kumpulan memori dimusnahkan, senarai terpaut ini akan dilalui mengikut urutan dan pengendali akan dipanggil semula satu demi satu. seorang untuk melakukan kerja pembersihan.
typedef void (*ngx_pool_cleanup_pt)(void *data); typedef struct ngx_pool_cleanup_s ngx_pool_cleanup_t; struct ngx_pool_cleanup_s { ngx_pool_cleanup_pt handler; void *data; ngx_pool_cleanup_t *next; };
Dapat dilihat bahawa banyak nod diperuntukkan daripada kumpulan memori, jadi anda boleh fokus pada data sebenar tanpa perlu risau tentang butiran lain.
/* * 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)
Seperti yang anda lihat daripada kod, kumpulan memori maksimum tidak melebihi saiz saiz halaman
Fungsi peruntukan dibahagikan kepada penjajaran memori dan salah jajaran memori, tetapi ini hanya mengawal peruntukan ruang dalam kolam memori dan tidak mengawal peruntukan memori yang besar.
(1) Peruntukkan ruang kecil
Penjajaran memoringx_palloc
Salah jajaran memoringx_pnalloc
ngx_pool_t * ngx_create_pool(size_t size, ngx_log_t *log) { ngx_pool_t *p; p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log); if (p == NULL) { return 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); 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; }
Apabila ruang yang perlu diperuntukkan kurang daripada maksimum, kaedah peruntukan memori kecil akan digunakan (iaitu, memperuntukkan ruang dari kolam memori Berbanding dengan ngx_palloc, ngx_pnalloc hanya mempunyai parameter terakhir apabila memanggil ngx_palloc_small 0.
Mula melintasi kolam memori yang ditunjuk oleh kolam->semasa, cari ruang yang memenuhi saiz peruntukan, dan jika ditemui, kembalikan alamat pertama
void * ngx_palloc(ngx_pool_t *pool, size_t size) { #if !(NGX_DEBUG_PALLOC) if (size <= pool->max) { return ngx_palloc_small(pool, size, 1); } #endif return ngx_palloc_large(pool, size); }
Apabila syarat peruntukan tidak dapat ditemui dalam kumpulan memori sedia ada , cipta kumpulan memori baharu
static ngx_inline void * ngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align) { u_char *m; ngx_pool_t *p; p = pool->current; do { m = p->d.last; if (align) { m = ngx_align_ptr(m, NGX_ALIGNMENT); } 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); }
Antaranya, selepas mencipta kumpulan memori baharu, satu lagi traversal dilakukan, dan kiraan yang gagal ditambah satu apabila lebih daripada 4 , kolam memori ini akan dilangkau dan akan digunakan pada masa akan datang. Jangan mulakan carian daripadanya.
Adalah dianggap bahawa anda tidak boleh memenuhi peruntukan lebih daripada 4 kali, dan anda tidak akan dapat memenuhi peruntukan pada masa hadapan. Anda tidak akan digunakan lagi peruntukan
(2) Peruntukkan ruang yang besar
static void * ngx_palloc_block(ngx_pool_t *pool, size_t size) { u_char *m; size_t psize; ngx_pool_t *p, *new; 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; m += sizeof(ngx_pool_data_t); m = ngx_align_ptr(m, NGX_ALIGNMENT); new->d.last = m + size; for (p = pool->current; p->d.next; p = p->d.next) { if (p->d.failed++ > 4) { pool->current = p->d.next; } } p->d.next = new; return m; }
Adalah dapat dilihat bahawa untuk mengelakkan peruntukan ruang, rantai besar dilalui untuk mencari nod yang boleh digunakan semula Walau bagaimanapun, jika senarai terpaut juga besar, ia mungkin terlalu perlahan, jadi hanya tiga yang pertama dicari Jika tiada satu pun daripada tiga dijumpai, ia diperuntukkan secara langsung ( Selain itu, nod juga diperuntukkan dari kolam memori, jadi semasa pembersihan berikutnya, anda tidak perlu. untuk menguruskan nod, anda hanya perlu melepaskan memori besar yang digunakan itu sendiri)
Penjajaran Memori
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) { if (large->alloc == NULL) { large->alloc = p; return p; } if (n++ > 3) { break; } } large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1); if (large == NULL) { ngx_free(p); return NULL; } large->alloc = p; large->next = pool->large; pool->large = large; return p; }
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_small(pool, sizeof(ngx_pool_large_t), 1); if (large == NULL) { ngx_free(p); return NULL; } large->alloc = p; large->next = pool->large; pool->large = large; return p; }
Ia boleh dilihat bahawa hanya satu nod diperuntukkan di sini, dan pengendali dan data data tidak ditetapkan, jadi ia bergantung pada pemanggil khusus untuk tetapan, kerana nod yang diperuntukkan dikembalikan di sini.
Sebagai contoh, dalam fungsi ngx_create_temp_file
,
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)); if (c == NULL) { return NULL; } if (size) { c->data = ngx_palloc(p, size); if (c->data == NULL) { return NULL; } } else { c->data = NULL; } c->handler = NULL; c->next = p->cleanup; p->cleanup = c; ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "add cleanup: %p", c); return c; }
menjana fail sementara, mendaftarkan fd dan nama fail dalam tugas pembersihan, dan tiada pemprosesan khas diperlukan jika fail berikutnya tidak digunakan, dan kolam memori dilepaskan akan dibersihkan secara seragam.
Keluarkan memori besar
Tetapkan semula yang terakhir dalam ingatan
Tetapkan semula kiraan yang gagal
ngx_int_t ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path, ngx_pool_t *pool, ngx_uint_t persistent, ngx_uint_t clean, ngx_uint_t access) { ... cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t)); if (cln == NULL) { return NGX_ERROR; } ... file->fd = ngx_open_tempfile(file->name.data, persistent, access); ... if (file->fd != NGX_INVALID_FILE) { cln->handler = clean ? ngx_pool_delete_file : ngx_pool_cleanup_file; clnf = cln->data; clnf->fd = file->fd; clnf->name = file->name.data; clnf->log = pool->log; return NGX_OK; } ... }
Terdapat fenomena di sini:
Apabila ruang tidak mencukupi dalam kolam memori, ngx_palloc_block
akan dipanggil untuk mencipta kumpulan memori baharu . Dan terakhir menunjuk ke m += sizeof(ngx_pool_data_t);
, jadi kumpulan memori yang baru diperuntukkan pada masa ini akan lebih besar daripada saiz kumpulan memori pertama yang tersedia (maks, semasa, rantai, besar, pembersihan, log Saiz medan ini (mungkin tidak sebanyak itu, kerana ia perlu dijajarkan, ia mungkin betul-betul sama selepas penjajaran), dan kini apabila menetapkan semula, p->d.last = (u_char *) p + sizeof(ngx_pool_t);
saiz yang tersedia bagi setiap kumpulan memori menjadi sama semula.
Tugas pembersihan panggil balik
Lepaskan memori yang besar
void ngx_destroy_pool(ngx_pool_t *pool) { 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->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) { if (l->alloc) { ngx_free(l->alloc); } } for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) { ngx_free(p); if (n == NULL) { break; } } }
通过遍历找到要释放的节点,将内存释放,并且将alloc设置成NULL,则有了节点重用的情况。
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; }
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; }
正常分配的空间中都是垃圾数据,所以当前函数在分配空间后,将分配的空间清零。
(1) 手动关闭指定fd
遍历清理任务,找到ngx_pool_cleanup_file的handler,如果是要关闭的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; 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; } } } }
(2) 关闭fd
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); } }
(3) 删除文件并关闭fd
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); } }
Atas ialah kandungan terperinci Bagaimana untuk melaksanakan kolam memori nginx. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!