1 进程内内存池
内存池关系如下图,接收请求后创建内存池,注意分为请求内存池,连接内存池,若开启keepalive,则多个请求是同一个连接内存池。
//ngx_pool_data_t
++-------+ +--------+
+---->|last | +---> last |
//ngx_pool_s | ++-------+ | +--------+
+-------------+ | | end | | | end |
| last +----+ | +--------+ | ++-------+
+-------------+ | | | next +-------+ ||next |
+--+ end | | | +--------+ ++-------+
| +-------------+ | | | failed | | failed |
| | next +----+-----+ +--------+ +--------+
| +-------------+ | | 已使用 | | 已使用 |
| | failed | | | | | |
| +-------------+ | +--------+ +--------+
| | max | | | 未使用 | | 未使用 |
| +-------------+ | +--------+ +--------+
| | current | |
| +-------------+ |
| | chain | | //ngx_pool_large_t
| +-------------+ | +--------+ +--------+
| | large +----+----------> next +--------->| next |
| +-------------+ | +--------+ +--------+
| | cleanup | | | alloc | | alloc |
| +-------------+ | +--------+ +--------+
| | log | | | |
| +-------------+ | +--------+ +--------+
| | 已使用 +-+--+ | | | |
| +---------<---+-+ | 内存 | | 内存 |
| | | | | | |
+--> 未使用 | +--------+ +--------+
+-------------+
//代码地址
//src/core/ngx_palloc.h
//src/core/ngx_palloc.c
//内存池
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_create_pool创建内存池,然后通过ngx_palloc、ngx_pcalloc、ngx_pnalloc分配内存,ngx_palloc是基础api,ngx_pcalloc对内存初始化,ngx_pnalloc为内存对齐 下面我们重点讲解ngx_palloc
ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);
ngx_palloc(ngx_pool_t *pool, size_t size)
{
//如果内存池的最大内存小于size,则分配ngx_pool_large_s大内存
if (size <= pool->max) {
return ngx_palloc_small(pool, size, 1);
}
return ngx_palloc_large(pool, size);
}
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);
}
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) {
//失败次数,比如分配4096内存,已经用了4090,申请7字节,失败了申请新的内存块,直到ngx_uint_t>4(经验值)后,
//pool->current指向后一个ngx_pool_data_t,当前剩余的6字节不会再被使用
if (p->d.failed++ > 4) {
pool->current = p->d.next;
}
}
//将新内存块添加到内存池链表上
p->d.next = new;
return m;
}
2 nginx共享内存
2.1 共享内存创建释放
//src/os/unix/ngx_shmem.h
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);
2.2 共享内存管理
//src/core/ngx_slab.h
//src/core/ngx_slab.c
typedef struct ngx_slab_page_s ngx_slab_page_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;
typedef struct {
ngx_shmtx_sh_t lock;
size_t min_size; //最小可分配的内存
size_t min_shift; //最小可分配内存对应的偏移min_size>>min_shift
ngx_slab_page_t *pages; //指向第一页
ngx_slab_page_t *last; //指向最后一页
ngx_slab_page_t free; //管理空闲页面
ngx_slab_stat_t *stats; //slab统计信息
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;


2.2 互斥锁
如果系统支持原子操作,Nginx可通过原子操作实现互斥锁,将共享内存块中原子变量的值改为当前进程pid;如果系统不支持原子操作,Nginx则通过文件锁实现互斥锁
checking for posix_memalign() ... found
checking for memalign() ... found
checking for mmap(MAP_ANON|MAP_SHARED) ... found
checking for mmap("/dev/zero", MAP_SHARED) ... found
checking for System V shared memory ... found
checking for POSIX semaphores ... not found
checking for POSIX semaphores in libpthread ... found
//todo 后面单独补充,本章重点内存
typedef struct {
#if (NGX_HAVE_ATOMIC_OPS)
ngx_atomic_t *lock; //原子变量
#if (NGX_HAVE_POSIX_SEM)
ngx_atomic_t *wait; //原子变量
ngx_uint_t semaphore; //信号量
sem_t sem;
#endif
#else
//文件锁
ngx_fd_t fd;
u_char *name;
#endif
ngx_uint_t spin; //自旋次数
} ngx_shmtx_t;