Apache 中使用的 APR Memory Pool 分析

Apache 中使用的 APR Memory Pool 分析

简介
APR 中的Memory Pool是内存管理基础模块,所有其他的模块,只要用到的内存分配,都要用到这个模块,相关的结构中都有apr_pool_t参数
pool本身并不直接从物理内存中分配或释放,而是通过allocator(内存分配器)来统一管理,可以为新池创建新的allocator(内存分配器),但通常使用默认的全局allocator(内存分配器),这样更有助于统一的内存管理
pool采用的是树形的结构,在初始化内存池(apr_pool_initialize)时,建立根池,和全局allocator(内存分配器),以后建立的都是根结点的子孙结点
可以从pool中分配任何大小的内存块,但释放的单位为pool,就是说pool释放之前,从pool分配出的内存不能单独释放,看起来好像有点浪费.
这里要注意的是,有些分配的内存块,清除时有特别操作,这样就需要要带清除函数,在分配之后用apr_pool_cleanup_register注册清除时用的函数。
特殊的,如果内存块里是线程对象,也不能用一般的清除函数,应该用apr_pool_note_subprocess注册清除操作

[结构体]
//池的结构
struct apr_pool_t {
    apr_pool_t           *parent;    //父池结点
    apr_pool_t           *child;    //子池结点
    apr_pool_t           *sibling;    //兄弟池结点
    apr_pool_t          **ref;        //指向-指向自已的指针,一般是前继结点的next指针
    cleanup_t            *cleanups;    //需要清除函数来销毁的内存块指针列表,通过注册来加入的
    cleanup_t            *free_cleanups;//当Kill cleanups中的一个项时,放到这里,注册一个新的项时,先从这里利用
    apr_allocator_t      *allocator;    //内存分配器,一般就用默认的,所有池共享一个内存分配器
    struct process_chain *subprocesses;    //子线程
    apr_abortfunc_t       abort_fn;       
    apr_hash_t           *user_data;
    const char           *tag;

#if !APR_POOL_DEBUG
    apr_memnode_t        *active;    //拥有的内存结点
    apr_memnode_t        *self; /* The node containing the pool itself */
    char                 *self_first_avail;

#else /* APR_POOL_DEBUG */
    apr_pool_t           *joined; /* the caller has guaranteed that this pool
                                   * will survive as long as ->joined */
    debug_node_t         *nodes;
    const char           *file_line;
    apr_uint32_t          creation_flags;
    unsigned int          stat_alloc;
    unsigned int          stat_total_alloc;
    unsigned int          stat_clear;
#if APR_HAS_THREADS
    apr_os_thread_t       owner;
    apr_thread_mutex_t   *mutex;
#endif /* APR_HAS_THREADS */
#endif /* APR_POOL_DEBUG */
#ifdef NETWARE
    apr_os_proc_t         owner_proc;
#endif /* defined(NETWARE) */
};

[函数]
对系统内存池初始化,全局的,一个进程只要初始化一次
apr_status_t     apr_pool_initialize (void)
销毁内存池对象,及内部的结构和子内存池
void         apr_pool_terminate (void)

创建一个新的内存池
apr_status_t     apr_pool_create_ex (apr_pool_t **newpool, apr_pool_t *parent, apr_abortfunc_t abort_fn, apr_allocator_t *allocator)
创建一个新的内存池,apr_pool_create_ex的使用默认参数简化版
apr_status_t     apr_pool_create (apr_pool_t **newpool, apr_pool_t *parent)
获取内存池使用的内存分配器
apr_allocator_t *     apr_pool_allocator_get (apr_pool_t *pool)
清除一个内存池的内容,清除后内容为空,但可以再使用
void             apr_pool_clear (apr_pool_t *p)
释构一个内存池
void             apr_pool_destroy (apr_pool_t *p)

从池中分配内存
void *             apr_palloc (apr_pool_t *p, apr_size_t size)
从池中分配内存,并将分配出来的内存置0
void *             apr_pcalloc (apr_pool_t *p, apr_size_t size)

设置内存分配出错时的调用函数
void             apr_pool_abort_set (apr_abortfunc_t abortfunc, apr_pool_t *pool)
获取内存分配出错时的调用函数
apr_abortfunc_t     apr_pool_abort_get (apr_pool_t *pool)

获取池的父池
apr_pool_t *     apr_pool_parent_get (apr_pool_t *pool)

判断a是否是b的祖先
int             apr_pool_is_ancestor (apr_pool_t *a, apr_pool_t *b)

为内存池做标签
void             apr_pool_tag (apr_pool_t *pool, const char *tag)

设置与当前池关联的数据
apr_status_t     apr_pool_userdata_set (const void *data, const char *key, apr_status_t(*cleanup)(void *), apr_pool_t *pool)
设置与当前池关联的数据,与apr_pool_userdata_set类似,但内部不拷贝数据的备份,如常量字符串时就有用
apr_status_t     apr_pool_userdata_setn (const void *data, const char *key, apr_status_t(*cleanup)(void *), apr_pool_t *pool)
获取与当前池关联的数据
apr_status_t     apr_pool_userdata_get (void **data, const char *key, apr_pool_t *pool)
注册内存块的清除函数,每块销毁时要特别处理的都要注册下,在cleanups里加入一个项
void             apr_pool_cleanup_register (apr_pool_t *p, const void *data, apr_status_t(*plain_cleanup)(void *), apr_status_t(*child_cleanup)(void *))
删除内存块的清除函数,从cleanups里移除一个项,放入free_cleanups中
void             apr_pool_cleanup_kill (apr_pool_t *p, const void *data, apr_status_t(*cleanup)(void *))
用新的child_cleanup,替换原来老的child_cleanup
void             apr_pool_child_cleanup_set (apr_pool_t *p, const void *data, apr_status_t(*plain_cleanup)(void *), apr_status_t(*child_cleanup)(void *))
执行内存块的清除函数,进从清除函数的队列cleanups中删除
apr_status_t     apr_pool_cleanup_run (apr_pool_t *p, void *data, apr_status_t(*cleanup)(void *))
一个空的内存块清除函数
apr_status_t     apr_pool_cleanup_null (void *data)
执行所有的子清除函数child_cleanup
void             apr_pool_cleanup_for_exec (void)

带调试信息内存池函数,功能跟上面的一样,只是多了调试信息
apr_status_t     apr_pool_create_ex_debug (apr_pool_t **newpool, apr_pool_t *parent, apr_abortfunc_t abort_fn, apr_allocator_t *allocator, const char *file_line)
void             apr_pool_clear_debug (apr_pool_t *p, const char *file_line)
void             apr_pool_destroy_debug (apr_pool_t *p, const char *file_line)
void *             apr_palloc_debug (apr_pool_t *p, apr_size_t size, const char *file_line)
void *             apr_pcalloc_debug (apr_pool_t *p, apr_size_t size, const char *file_line)

<一>内存池初始化 apr_pool_initialize
1.一个进程只在第一次调用时初始化,后面只记录调用的次数
    if (apr_pools_initialized++)
        return APR_SUCCESS;
2.创建全局内存分配器(global_allocator)
      if ((rv = apr_allocator_create(&global_allocator)) != APR_SUCCESS) {
        apr_pools_initialized = 0;
        return rv;
    }
3.创建全局内存池,根内存池
      if ((rv = apr_pool_create_ex(&global_pool, NULL, NULL,
                                 global_allocator)) != APR_SUCCESS) {
        apr_allocator_destroy(global_allocator);
        global_allocator = NULL;
        apr_pools_initialized = 0;
        return rv;
    }
4.为内存池做标记
   apr_pool_tag(global_pool, "apr_global_pool");
5.初始化原子操作
有什么想法可以发邮件 fastxyf at hotmail.com,一起交流
6.对于支持多线程的系统,还创建互拆体,并设置到内存分配器
#if APR_HAS_THREADS
    {
        apr_thread_mutex_t *mutex;

        if ((rv = apr_thread_mutex_create(&mutex,
                                          APR_THREAD_MUTEX_DEFAULT,
                                          global_pool)) != APR_SUCCESS) {
            return rv;
        }

        apr_allocator_mutex_set(global_allocator, mutex);
    }
#endif /* APR_HAS_THREADS */

7.设置内存分配器的池所有者
    apr_allocator_owner_set(global_allocator, global_pool);

<二>销毁内存池对象 apr_pool_terminate
1.如果调用了多次内存池初始化,则只有在调用了相应次数的销毁时,最后一次才进行操作
    if (!apr_pools_initialized)
        return;
    if (--apr_pools_initialized)
        return;
2.释放全局内存池,同时会释放想应的互拆体,如果内存分配器的所有者是本内存池,则一同释放内存分配器
    apr_pool_destroy(global_pool);

<三>创建一个新的内存池 apr_pool_create_ex
参数说明:
    newpool     新创建的内存池指针,用于返回的.
    parent         父内存池. 如果为空,则父内存池为根内存池,非空时,新的内存池继承父内存池的属性.
    abort_fn     分配内存失败时的回调函数,中断函数
    allocator     新内存池所用的内存分配器. NULL则使用父内存池的内存分配器
1.先确定内存池的参数,如父结点,出错处理函数,所用的内存分配器
    if (!parent)
        parent = global_pool;
    if (!abort_fn && parent)
        abort_fn = parent->abort_fn;
    if (allocator == NULL)
        allocator = parent->allocator;
2.从内存分配器一块内存块,用于存贮内存池的信息,如果内存分配失败,则调用中断函数
    if ((node = allocator_alloc(allocator,
                                MIN_ALLOC - APR_MEMNODE_T_SIZE)) == NULL) {
        if (abort_fn)
            abort_fn(APR_ENOMEM);

        return APR_ENOMEM;
    }
3.用新分配内存块,存贮内存池的结构apr_pool_t
    node->next = node;
    node->ref = &node->next;

    pool = (apr_pool_t *)node->first_avail;
    node->first_avail = pool->self_first_avail = (char *)pool + SIZEOF_POOL_T;

    pool->allocator = allocator;
    pool->active = pool->self = node;
    pool->abort_fn = abort_fn;
    pool->child = NULL;
    pool->cleanups = NULL;
    pool->free_cleanups = NULL;
    pool->subprocesses = NULL;
    pool->user_data = NULL;
    pool->tag = NULL;
  物理内存块的内容如下
           |----------------|
           | apr_memnode_t  | 占用大小 APR_MEMNODE_T_SIZE
           | ---------------|
           | apr_pool_t     | 占用大小 SIZEOF_POOL_T
           | ---------------|
           |   可用空间     | 占用大小 8K - APR_MEMNODE_T_SIZE - SIZEOF_POOL_T
           | ---------------|
4.系统支持多线程,则创建互拆体.
5.设置兄弟结点,及父结点
        if ((pool->sibling = parent->child) != NULL)
            pool->sibling->ref = &pool->sibling;

        parent->child = pool;
        pool->ref = &parent->child;

apr_pool_create 是使用了默认的内存分配器,和中断函数的apr_pool_create_ex简化版
#define apr_pool_create(newpool, parent) /
    apr_pool_create_ex(newpool, parent, NULL, NULL)

<四> 从池中分配一块内存 apr_palloc
参数说明:
    pool        要分配内存的池
    size        要分配的内存大小
1.先按8字节对齐size
    size = APR_ALIGN_DEFAULT(size);
2. 如果首结点有足够的空闲空间,则从这里分配
    if (size < (apr_size_t)(active->endp - active->first_avail)) {
        mem = active->first_avail;
        active->first_avail += size;

        return mem;
    }
3.跟链表首结点比较(因为链表结点是按空闲空间的大小,由多至少排序的,所以首结点不够,后面的也不用比了),空间够就从链表头结点分配,因为分配后链表头结点空闲空间不一定还最多了,所以要取出,后面重新插入
  如果链表首结点空间不够,则调用内存分配器,新分配一块内存
 
    if (size < (apr_size_t)(node->endp - node->first_avail)) {
        list_remove(node);
    }
    else {
        if ((node = allocator_alloc(pool->allocator, size)) == NULL) {
            if (pool->abort_fn)
                pool->abort_fn(APR_ENOMEM);

            return NULL;
        }
    }
4.将首结点或新分配的结点node,插到队列首部,再根据node里的空间大小,选择插到的位置
     这里没看懂,为什么要插入?到后面找又先移出,再插到正确位置.不能改成直接在后面一次插入?
    list_insert(node, active);

    pool->active = node;

    free_index = (APR_ALIGN(active->endp - active->first_avail + 1,
                            BOUNDARY_SIZE) - BOUNDARY_SIZE) >> BOUNDARY_INDEX;

    active->free_index = (APR_UINT32_TRUNC_CAST)free_index;
    node = active->next;
    if (free_index >= node->free_index)
        return mem;

    do {
        node = node->next;
    }
    while (free_index < node->free_index);

    list_remove(active);
    list_insert(active, node);

apr_pcalloc 是在apr_palloc分配的内存上,增加了对分配的内存块置0
    size = APR_ALIGN_DEFAULT(size);
    if ((mem = apr_palloc(pool, size)) != NULL) {
        memset(mem, 0, size);
    }

<五>清空池内容,释放占用的内存,但保留池对象 apr_pool_clear
1.如果存在子池,则释放所有子池
    while (pool->child)
        apr_pool_destroy(pool->child);
2.调用清除函数,清除注册了销毁函数的内存块,每块需要销毁函数的内存块都要单独注册
    run_cleanups(&pool->cleanups);
3.清除包括子线程的内存块
    free_proc_chain(pool->subprocesses);
4.清除附在本内存池上的内存块,回到刚分配内存池的状态
    *active->ref = NULL;
    allocator_free(pool->allocator, active->next);
    active->next = active;
    active->ref = &active->next;

<六>释构一个内存池 apr_pool_destroy
  执行的操作和apr_pool_clear差不多,增加了删除互拆体,和释放内存池结构占用的块
  如果所用的内存分配器的所有者,是本内存池,也一并析构
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值