<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    不定長內存池之apr_pool

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接

    內存池可有效降低動態申請內存的次數,減少與內核態的交互,提升系統性能,減少內存碎片,增加內存空間使用率,避免內存泄漏的可能性,這么多的優點,沒有理由不在系統中使用該技術。

    內存池分類:

    1、              不定長內存池。典型的實現有apr_pool、obstack。優點是不需要為不同的數據類型創建不同的內存池,缺點是造成分配出的內存不能回收到池中。這是由于這種方案以session為粒度,以業務處理的層次性為設計基礎。

    2、             定長內存池。典型的實現有LOKI、BOOST。特點是為不同類型的數據結構分別創建內存池,需要內存的時候從相應的內存池中申請內存,優點是可以在使用完畢立即把內存歸還池中,可以更為細粒度的控制內存塊。
        與變長的相比,這種類型的內存池更加通用,另一方面對于大量不同的數據類型環境中,會浪費不少內存。但一般系統主要的數據結構都不會很多,并且都是重復申請釋放使用,這種情況下,定長內存池的這點小缺點可以忽略了。

    一、apr

    apr 是 apache 使用的底層庫,apache 是跨平臺的,其跨平臺正是基于 apr。個人覺得,使用apr有兩個好處,一是不用擔心跨平臺(事實上,我從來就不擔心,因為我寫的程序,從來都不跨平臺)。二是 apr 的 pool 很好用。pool 有兩個好處,一是可以理解成內存池,在 pool上 分配的內存,是不用釋放的,pool 銷毀的時候自然會釋放這些內存(所以銷毀(清理)pool變得異常重要,千萬不能忘了)。二是可以理解成資源管理器,分配資源后,然后在pool上注冊一個釋放資源的函數,pool 銷毀(清理)的時候,會調用這個函數,釋放資源。例如打開了一個文件,可以在 pool 上注冊一個關閉文件的函數,讓 pool 替你關閉文件。也可以在不銷毀(清理)pool 時,手動的釋放。具體可以看參考apr手冊

    APR的核心就是Apache的資源管理(池),我們將在本章的后面部分進行更加詳細的介紹。表3-1列出了APR中的所有模塊。

    表3-1  APR模塊

    名稱

    目的

    apr_allocator

    內存分配,內部使用

    apr_atomic

    原子操作

    apr_dso

    動態加載代碼(.so/.dll)

    apr_env

    讀取/設定環境變量

    apr_errno

    定義錯誤條件和宏

    apr_file_info

    文件系統對象和路徑的屬性

    apr_file_io

    文件系統輸入/輸出

    apr_fnmatch

    文件系統模式匹配

    apr_general

    初始化/終結,有用的宏

     

    名稱

    目的

    apr_getopt

    命令參數

    apr_global_mutex

    全局鎖

    apr_hash

    哈希表

    apr_inherit

    文件句柄繼承助手

    apr_lib

    奇數和末端

    apr_mmap

    內存映射

    apr_network_io

    網絡輸入/輸出(套接字)

    apr_poll

    投票

    apr_pools

    資源管理

    apr_portable

    APR到本地映射轉換

    apr_proc_mutex

    進程鎖

    apr_random

    隨機數

    apr_ring

    環數據結構和宏

    apr_shm

    共享內存

    apr_signal

    信號處理

    apr_strings

    字符串操作

    apr_support

    內部支持函數

    apr_tables

    表格和數組函數

    apr_thread_cond

    線程條件

    apr_thread_mutex

    線程鎖

    apr_thread_proc

    線程和進程函數

    apr_thread_rwlock

    讀寫鎖

    apr_time

    時間/日期函數

    apr_user

    用戶和組ID服務

    apr_version

    APR版本

    apr_want

    標準頭文件支持

    表3-2  APU模塊

    名稱

    目的

    apr_anylock

    透明的、任何鎖的封裝

    apr_base64

    Base-64編碼

    apr_buckets

    Buckets/Bucket brigade

    apr_date

    時間字符串解析

    apr_dbd

    針對SQL數據庫的常用API

    apr_dbm

    針對DBM數據庫的常用API

    apr_hooks

    鉤子實現宏

    apr_ldap

    LDAP授權API

    apr_ldap_init

    LDAP初始化API,主要應用在和LDAP服務器的初始安全連接

    apr_ldap_option

    設置LDAP選項的API

    apr_ldap_url

    解析和處理LDAP URL的API

    apr_md4

    MD4編碼

    apr_md5

    MD5編碼

    apr_optional

    可選函數

    apr_optional_hooks

    可選鉤子

    apr_queue

    線程安全的FIFO隊列

    apr_reslist

    資源池

    apr_rmm

    可再定位地址的內存

     

    名稱

    目的

    apr_sdbm

    SDBM庫

    apr_sha1

    SHA1編碼

    apr_strmatch

    字符串模式匹配

    apr_uri

    URI解析/構造

    apr_uuid

    用戶標識

    apr_xlate

    字符集轉換(I18N)

    apr_xml

    XML解析

         

    基本的約定

    APR和APR-UTIL采用了一些約定,使得它們的API具有同質性,并且易于使用。

    3.3.1  參考手冊:API文檔和Doxygen

    APR和APU在代碼層都有非常好的文檔。每一個公開函數和數據類型都在定義它們的頭文件中進行了注釋,使用了doxygen友好的格式。那些頭文件,或者doxygen生成的文檔,為程序員提供了完整的API參考手冊。如果你安裝了doxygen,那么就可以通過make dox命令從源代碼中生成你自己版本的APR參考手冊。

    3.3.2  命名空間

    所有的APR和APU的公開接口都使用了字符串前綴“apr_”(數據類型和函數)和“APR_”(宏),這就為APR定義了一個“保留”的命名空間。

    在APR命名空間中,絕大部分的APR和APU模塊使用了二級命名空間。這個約定通常基于正在討論的那個模塊的名字。例如,模塊apr_dbd中的所有函數使用字符串“apr_dbd_”前綴。有時候使用一個明顯的描述性的二級命名空間。例如,在模塊apr_network_io中套接字操作使用“apr_socket_”前綴。

    3.3.3  聲明的宏

    APR和APU的公開函數使用類似于APR_DECLARE、APU_DECLARE和APR_ DECLARE_NONSTD的宏進行聲明。例如:

    APR_DECLARE(apr_status_t) apr_initialize(void);

    在很多的平臺上,這是一個空聲明,并且擴展為

    apr_status_t apr_initialize(void);

    例如在Windows的Visual C++平臺上,需要使用它們自己的、非標準的關鍵字,例如“_dllexport”來允許其他的模塊使用一個函數,這些宏就需要擴展以適應這些需要的關鍵字。

    3.3.4  apr_status_t和返回值

    在APR和APU中廣泛采用的一個約定是:函數返回一個狀態值,用來為調用者指示成功或者是返回一個錯誤代碼。這個類型是apr_status_t,在apr_errno.h中定義,并賦予整數值。因此一個APR函數的常見原型就是:

    APR_DECLARE(apr_status_t) apr_do_something(…function args…);

    返回值應當在邏輯上進行判斷,并且實現一個錯誤處理函數(進行回復或者對錯誤進行進一步的描述)。返回值APR_SUCCESS意味著成功,我們通常可以用如下的方式進行錯誤處理結構:

    apr_status_t rv;
    ...
    rv = apr_do_something(... args ...);
    if (rv != APR_SUCCESS) {
    /* 記錄一個錯誤 */
    return rv;
    }

    有時候我們可能需要做得更多。例如,如果do_something是一個非閉塞的I/O操作并且返回APR_EAGAIN,我們可能需要重試這個操作。
    有些函數返回一個字符串(char *或者const char *)、一個void *或者void。這些函數就被認為在沒有失敗條件或者在錯誤發生時返回一個空指針。

    3.3.5  條件編譯

    本質上說,APR的一些特色可能并不是每個平臺都支持的。例如,FreeBSD在5.x版本之前并沒有適合Apache的本地線程實現,因此線程在APR中就不被支持(除非編譯時手動設置相應的操作)。

    為了在這種情況下應用程序依然能夠工作,APR為這些情況提供了APR_HAS_*宏。如果一個應用處于這種情況,它應當使用這些宏進行條件編譯。例如,一個模塊執行了一個操作,這個操作可能導致在多線程環境下的競爭條件,那么它就可能使用以下的方式。

    #if APR_HAS_THREADS
    rv = apr_thread_mutex_lock(mutex);
    if (rv != APR_SUCCESS) {
    /* 記錄一個錯誤 */
    /* 放棄關鍵的操作*/
    }
    #endif

        /* ... 在這里執行關鍵代碼... */

    #if APR_HAS_THREAD
    apr_thread_mutex_unlock(mutex);
    #endif

     

    二、apr_pool內存池。

    pool本身并不直接從物理內存中分配或釋放,而是通過allocator(內存分配器)來統一管理,可以為新池創建新的allocator(內存分配器),但通常使用默認的全局allocator(內存分配器),這樣更有助于統一的內存管理。pool采用的是樹形的結構,在初始化內存池(apr_pool_initialize)時,建立根池,和全局allocator(內存分配器),以后建立的都是根結點的子孫結點可以從pool中分配任何大小的內存塊,但釋放的單位為pool,就是說pool釋放之前,從pool分配出的內存不能單獨釋放,看起來好像有點浪費。這里要注意的是,有些分配的內存塊,清除時有特別操作,這樣就需要要帶清除函數,在分配之后用apr_pool_cleanup_register注冊清除時用的函數。特殊的,如果內存塊里是線程對象,也不能用一般的清除函數,應該用apr_pool_note_subprocess注冊清除操作。

    apr_pool中主要有3個對象,allocator、pool、block。pool從allocator申請內存,pool銷毀的時候把內存歸還allocator,allocator銷毀的時候把內存歸還給系統,allocator有一個owner成員,是一個pool對象,allocator的owner銷毀的時候,allocator被銷毀。在apr_pool中并無block這個單詞出現,這里大家可以把從pool從申請的內存稱為block,使用apr_palloc申請block,block只能被申請,沒有釋放函數,只能等pool銷毀的時候才能把內存歸還給allocator,用于allocator以后的pool再次申請。

    常見函數:

    對系統內存池初始化,全局的,一個進程只要初始化一次

    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)

     

        一般可以不調用創建allocator的函數,而是使用的默認全局allocator。但是apr_pool提供了一系列函數操作allocator,可以自己調用這些函數:

    apr_allocator_create

    apr_allocator_destroy

    apr_allocator_alloc

    apr_allocator_free  創建銷毀allocator

    apr_allocator_owner_set

    apr_allocator_owner_get  設置獲取owner

    apr_allocator_max_free_set 設置pool銷毀的時候內存是否直接歸還到操作系統的閾值

    apr_allocator_mutex_set

    apr_allocator_mutex_get  設置獲取mutex,用于多線程

    apr_pool的一個大缺點就是從池中申請的內存不能歸還給內存池,只能等pool銷毀的時候才能歸還。為了彌補這個缺點,apr_pool的實際使用中,可以申請擁有不同生命周期的內存池。

     

    三、實例

    #include "stdafx.h"

    #include "apr_pools.h"

    #include <new>

    #pragma comment(lib,"libapr-1.lib")

     

    int main(int argc, char* argv[])

    {

        apr_pool_t *root;

        apr_pool_initialize();//初始化全局分配子(allocator),并為它設置mutext,以用于多線程環境,初始化全局池,指定全局分配子的owner是全局池

        apr_pool_create(&root,NULL);//創建根池(默認父池是全局池),根池生命期為進程生存期。分配子默認為全局分配子

        {

            apr_pool_t *child;

            apr_pool_create(&child,root);//創建子池,指定父池為root。分配子默認為父池分配子

           void *pBuff=apr_palloc(child,sizeof(int));//從子池分配內存

           int *pInt=new (pBuff)  int(5);//隨便舉例下基于已分配內存后,面向對象構造函數的調用。

            printf("pInt=%d/n",*pInt);

           {

               typedef struct StudentInfo{

                   char szName[20];

                   bool nSex;

               };

     

               apr_pool_t *grandson;

                apr_pool_create(&grandson,root);

               void *pBuff2=apr_palloc(grandson,sizeof(StudentInfo));

               StudentInfo *pSI=new (pBuff2)  StudentInfo();

               strcpy(pSI->szName,"zhangsan");

               pSI->nSex = 1;

               printf("Name=%s,sex=%d/n",pSI->szName,pSI->nSex);   

     

               apr_pool_destroy(grandson);

           }

            apr_pool_destroy(child);//釋放子池,將內存歸還給分配子

        }

        apr_pool_destroy(root);//釋放父池,

        apr_pool_terminate();//釋放全局池,釋放全局allocator,將內存歸還給系統

        return 0;

    }

     

     

    參考資料:

    apr官方網站:http://apr.apache.org/

    RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全