频繁进行malloc、free(向操作系统申请内存、把内存交还给操作系统)会拖慢速度,使用内存池可以批量申请与释放。
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;
};
typedef struct {
u_char *last;
u_char *end;
ngx_pool_t *next;
ngx_uint_t failed;
} ngx_pool_data_t;
struct ngx_pool_large_s {
ngx_pool_large_t *next;
void *alloc;
};
单块内存池是一个树型结构,由ngx_pool_t作为根节点,注意其包含一个ngx_pool_data_t d而非*d,自带一个小内存分配区,之后分配的data挂载到小内存分配区链表后,即data的next字段。创建内存池时不创建大内存分配区。
申请内存共封装了3个api:
void *
ngx_palloc(ngx_pool_t *pool, size_t size){
if (size <= pool->max) {
return ngx_palloc_small(pool, size, 1); //调用palloc small
}
return ngx_palloc_large(pool, 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) {
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;
}
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;
// align 使…成一直线,校准
// 对齐内存
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); //空闲空间不够,调用palloc_block
}
static void *
ngx_palloc_block(ngx_pool_t *pool, size_t size)
{
u_char *m;
size_t psize;
ngx_pool_t *p, *new;
// pool->d.end - (u_char *) pool为当前ngx_pool_t包含的第一个ngx_pool_data_t的所有空间大小,即分配了一整个ngx_pool_data_t,而非仅仅是size大小的内存。
psize = (size_t) (pool->d.end - (u_char *) pool);
m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log); //调用memalign分配空间
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; // 已分配区指针向后移动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;
}
#define ngx_memalign(alignment, size, log) ngx_alloc(size, log)
void *
ngx_alloc(size_t size, ngx_log_t *log)
{
void *p;
p = malloc(size); //最终由这一行向操作系统要空间
if (p == NULL) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
"malloc(%uz) failed", size);
}
ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, "malloc: %p:%uz", p, size);
return p;
}
可以看到分配内存被分化为分配小内存和分配大内存:
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;
}
#define ngx_free free
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);
}
}
// 释放所有large
for (l = pool->large; l; l = l->next) {
if (l->alloc) {
ngx_free(l->alloc);
}
}
// 释放pool_t、所有pool_data_t
for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
ngx_free(p);
if (n == NULL) {
break;
}
}
}
进程的内存是虚拟内存,由os分配物理内存,并维护映射表,实现虚拟到物理的映射;共享内存是让多个进程的虚拟内存映射到同一块物理内存,因此多个进程都可以读写这块内存,实现进程间通信。使用mmap、munmap申请和释放共享内存。
nginx共享内存由master进程创建,并记录共享内存地址,fork子进程时,子进程继承记录共享内存地址的变量,进而访问共享内存。
共享内存结构ngx_shm_t:
typedef struct {
u_char *addr;
size_t size;
ngx_str_t name;
ngx_log_t *log;
ngx_uint_t exists; /* unsigned exists:1; */
} ngx_shm_t;
ngx_int_t ngx_shm_alloc(ngx_shm_t *shm); // 创建
void ngx_shm_free(ngx_shm_t *shm); // 销毁
根据操作系统支持情况不同,采用不同方法(mmap或shmget)创建。linux支持的mmap:
ngx_int_t
ngx_shm_alloc(ngx_shm_t *shm)
{
shm->addr = (u_char *) mmap(NULL, shm->size,
PROT_READ|PROT_WRITE,
MAP_ANON|MAP_SHARED, -1, 0);
if (shm->addr == MAP_FAILED) {
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"mmap(MAP_ANON|MAP_SHARED, %uz) failed", shm->size);
return NGX_ERROR;
}
return NGX_OK;
}
void
ngx_shm_free(ngx_shm_t *shm)
{
if (munmap((void *) shm->addr, shm->size) == -1) {
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"munmap(%p, %uz) failed", shm->addr, shm->size);
}
}
共享内存通过原子变量标识一个锁,每个进程须先获取到锁才能访问其中的共享变量。每个进程内有一个ngx_shmtx_t结构体进行加锁、释放锁等操作。
typedef struct {
ngx_atomic_t lock;
} ngx_shmtx_sh_t;
typedef struct {
ngx_atomic_t *lock; // 指向共享内存锁变量的lock字段
ngx_uint_t spin; // 控制自旋次数
} ngx_shmtx_t;
ngx_int_t ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *addr, u_char *name);
void ngx_shmtx_destroy(ngx_shmtx_t *mtx);
ngx_uint_t ngx_shmtx_trylock(ngx_shmtx_t *mtx);
void ngx_shmtx_lock(ngx_shmtx_t *mtx);
void ngx_shmtx_unlock(ngx_shmtx_t *mtx);
ngx_uint_t ngx_shmtx_force_unlock(ngx_shmtx_t *mtx, ngx_pid_t pid);
通过共享内存池可以高效申请和释放共享内存。
typedef struct {
ngx_shmtx_sh_t lock;
size_t min_size;
size_t min_shift;
ngx_slab_page_t *pages;
ngx_slab_page_t *last;
ngx_slab_page_t free;
ngx_slab_stat_t *stats;
ngx_uint_t pfree;
u_char *start;
u_char *end;
ngx_shmtx_t mutex;
u_char *log_ctx;
u_char zero;
unsigned log_nomem:1;
void *data;
void *addr;
} ngx_slab_pool_t;
struct ngx_slab_page_s {
uintptr_t slab;
ngx_slab_page_t *next;
uintptr_t prev;
};
typedef struct {
ngx_uint_t total;
ngx_uint_t used;
ngx_uint_t reqs;
ngx_uint_t fails;
} ngx_slab_stat_t;
主要用于简单场景如计数。
struct ngx_shm_zone_s {
void *data;
ngx_shm_t shm; // 记录共享内存块的相关信息
ngx_shm_zone_init_pt init; // 共享内存初始化后的回调
void *tag;
void *sync;
ngx_uint_t noreuse; /* unsigned noreuse:1; */
};
master进程fork子进程时,子进程继承ngx_cycle_t,这样就获得了shared_memory。
struct ngx_cycle_s {
...
ngx_list_t shared_memory;
...
};