nginx内存管理

进程内内存管理:内存池

频繁进行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:

  • ngx_palloc:基本的api,申请内存的首地址对齐(首地址是4或8的整数倍),获取后不初始化
  • ngx_pcalloc:在palloc的基础上初始化
  • ngx_pnalloc:首地址不对齐
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;
}

可以看到分配内存被分化为分配小内存和分配大内存:

  • 分配小内存时首先检查pool_t的所有poll_data_t内是否还有空间,如果有直接分配,否则调用palloc_block再申请一块ngx_pool_data_t,然后再分配。
  • 分配大内存直接向操作系统申请,并且用于管理这块内存的ngx_pool_large_t所占用的空间是由palloc_small分配的,然后挂在pool_large_t链表后。
释放内存
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
  • 小内存无需手动释放,释放整个内存池时自动释放就可以。
  • 大内存释放时先找到要释放的那一块,然后直接调用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;
  • 一些锁操作api
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);
共享内存池

通过共享内存池可以高效申请和释放共享内存。

  • 每块共享内存有一个共享内存池,管理整个共享内存池的结构体是ngx_slab_pool_t,位于共享内存块头部:
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;
  • ngx_slab_page_t用于管理内存页,有9种规格
struct ngx_slab_page_s {
    uintptr_t         slab;
    ngx_slab_page_t  *next;
    uintptr_t         prev;
};
  • ngx_slab_stat_t用于统计信息,也有9种规格,配合page_t统计情况
typedef struct {
    ngx_uint_t        total;
    ngx_uint_t        used;

    ngx_uint_t        reqs;
    ngx_uint_t        fails;
} ngx_slab_stat_t;
共享内存的使用
  • 使用ngx_shm_alloc创建共享内存块,自行创建锁、自行管理,无需创建池

主要用于简单场景如计数。

  • 使用ngx_shared_memory_add创建共享内存块,使用ngx_slab_pool_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;
	  ...
};

你可能感兴趣的:(nginx源码阅读学习笔记,nginx,c语言)