Apache内存管理-内存池介绍与内存结点介绍(参照网络上其他资料与代码全景分析)

 

  通常程序下,C 言中内存的分配与 放是用 malloc与free。缺点很多, 开发 者可能因 疏忽而忘 记释 放。在 Apache 这样 的服 器中如果内存有泄漏是
不可被容忍的。而且频繁的malloc与free可能会造成页面错误。如果一直不释放会造成系统性能下降。                 
  因此Apache引入了内存池概念。内存池是一片很大的内存空间,一次性分配成功避免反 复的申 请释放。而且内存的释放会在内存销 毁的 时候释放。
  但如果内存池只有一个的话,那 这个内存池需要在Apache启动的时候申请,在Apache关闭的时候释放。这样显得不太合理。      
  因此Apache会使用多个内存池,采用分级的方法, 每一 级内存池的生命周期不同。                       
                                                         
首先,我需要了解一下Apache代码的结构:                                        
 apache可划分为四大层次:可移植运行库( APR)、核心功能 层( Apache CORE)、可 选功能层( Apache Optional Module)、第三方支持 库。   
 图例:                                                     
                                                         
                       Apche模 中的第三方支持                      
            OpenSSL  PHP …………………………………………………………………Perl LDAP           
                                                         
                          Apche可 功能                        
            mod_SSL mod_Php mod_rewrite ………………………………………mod_perl mod_ladp           
                                                         
                        Apche核心功能                          
            http_core http_request……http_protocol(Apache核心)      mod_so core MPM http(核心模块)         
                                                         
                        libapr (可移 运行 库层                       
            共享内存 文件I/O 网 I/O……                          libpcre libldap libexpat                
                                                         
                     操作系  (Win32、Unix、OS/2、MacOS、Netware)                
                                                         
  为OS不同,apache在处理跨平台的时候显然需要使用不同的系统API。从2.0开始,Apache将专门封装不同操作系统API的任务分离了出来,形成APR。
  这样对于所有的操作系统,提供了一个完全相同的函数接口。这样事实上,任何应用程序想要跨平台均可使用APR做为底层的支持。  
                                                         
其次,我需要了解一下内存池的分级情况:                                        
          
       
                 
                 
                 
                 
                 
                 
                 
                 
                 
                 
                 
                 
                 
                 
                 
                 
                 
  但需要注意的是pool并不会直接分配与释放内存,而是通过 allocator来 统一管理。 Pool采用的是 树形结构,如上图所示。在内存初始化时,使用函数
  apr_pool_initialize建立根池与全局的allocator,以后建立的都是根结点的子孙结点。可以从 pool中分配任何大小的内存。但需要在 释放 pool 时这些申请的内存
  才会被释放。需要注意 的是,有些分配的内存块,清除时有特别的操作,所以需要自带清除函数,在分配之后用apr_pool_cleanup_register注册清除时用的
  函数。特殊的是,如果内存块里是线程对象,不能用一般的清除函数,应该用apr_pool_note_subprocess注册清除操作。         
                                                         
接下来,需要看一下池的 构:(以下内容,摘自http://blog.csdn.net/fastxyf/archive/2006/06/30/855344.aspx)需要 亲自对代码中看一下。   
                                                         
  //池的                                                   
  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)                     
                                                         
  以下描述的应该是apache初始化内存池的一系统操作:                                 
                                                         
  <一>内存池初始化 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.初始化原子操作                                               
                                                         
  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差不多,增加了 除互拆体,和 放内存池 构占用的                       
  如果所用的内存分配器的所有者,是本内存池,也一并析构                                
                                                         
                                                         
 内存池的结构与相关函数了解了以后,应该了解一下内存结点的情况:                            
                                                         
 内存结点是Apache整个内存管理的基础,结构如下:                                    
                                                         
 /** basic memory node structure */                                         
 struct apr_memnode_t {                                              
     apr_memnode_t *next;               /**< next memnode */                                
     apr_memnode_t **ref;               /**< reference to self */                               
     apr_uint32_t   index;              /**< size */                                     
     apr_uint32_t   free_index;         /**< how much free */                                
     char          *first_avail;        /**< pointer to first free memory */                            
     char          *endp;               /**< pointer to end of free memory */                           
 };                                                       
                                                         
 该结构中,不同的结点之间采用next指针形成结点链表。在结点内部为了方便引用自身,成员中加入的ref,主要用来记录当前结点的首地址。
 该结构中没有存放实际分配的内存,它不会单独存在,依附于具体的内存分配单元。一旦实际内存分配后该结构置于整个单元的顶部。  
 如下图示,内存结点的情况:                                             
      
     
           
           
           
           
           
           
           
           
           
           
           
           
           
           
           
           
           
           
           
           
           
                                                         
                                                         
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值