### 使用內存池編程
幾乎每一個使用過C語言的開發者曾經感嘆令人畏縮的內存管理,分配足夠的內存,并且追蹤內存的分配,在不需要時釋放內存―這個任務會非常復雜。當然,如果沒有正確地做到這一點會導致程序毀掉自己,或者更加嚴重一點,把電腦搞癱。幸運的是,Subversion所依賴的APR庫為了移植性提供了apr_pool_t類型,代表了應用可以分配內存的池。
一個內存池是程序所需要分配內存的一個抽象表示,不選擇使用標準的`malloc()`從操作系統直接申請內存,而使用向APR申請的池申請創建的(使用`apr_pool_create()`方法)內存。APR會從操作系統分配合適的內存塊這些內存可以立刻在程序里使用,當程序需要更多的池內存時,它會使用APR的池API方法,如`apr_palloc()`,返回池中的基本內存位置,這個程序可以繼續從池中請求內存,在超過最初的池的容量后,APR會自動滿足程序的要求擴大池的大小,直到系統沒有足夠的內存。
現在,如果這是池故事的結尾,我們就不應該再作過多的關注,很幸運,不是這個情況。池不可以僅僅被創建;它也可以被清空和銷毀,分別使用`apr_pool_clear()`和`apr_pool_destroy()`。這給了用戶靈活性來分配許多―或者是數千―東西自這個池,然后使用一個命令來清空!更進一步,池可以分級,你可以為前一步創建的池創建“子池”。當你清空一個池,所有的子池會被銷毀;如果你銷毀一個池,它和所有的子池也會被銷毀。
在我們進一步研究之前,開發者會發現在Subversion源代碼中并沒有對前面提到的APR池方法有很多的調用,APR提供了許多擴展機制,像使用自定義的附加到池的“用戶數據”的能力,注冊當池銷毀時的所要調用的清理方法的機制,Subversion使用一些不太瑣碎的方法來利用這些擴展,所以Subversion提供了(大多數代碼使用的)包裹方法`svn_pool_create()`、`svn_pool_clear()`和`svn_pool_destroy()`。
盡管池幫助我們基本的內存管理,池的創建確實投射出了循環和迭代場景,因為反復在循環中經常沒有界限,在深度迭代中,一定區域的內存消耗變得不可預料,很幸運,使用嵌套的內存池可以簡單的管理這種潛在的混亂情形,下面的例子描述了在這個情形下嵌套池的基本使用非常平常―迭代的對目錄樹的遍歷,對樹上的每一個部分做一些任務。
**例8.5.有效地池使用**
~~~
/* Recursively crawl over DIRECTORY, adding the paths of all its file
children to the FILES array, and doing some task to each path
encountered. Use POOL for the all temporary allocations, and store
the hash paths in the same pool as the hash itself is allocated in. */
static apr_status_t
crawl_dir (apr_array_header_t *files,
const char *directory,
apr_pool_t *pool)
{
apr_pool_t *hash_pool = files->pool; /* array pool */
apr_pool_t *subpool = svn_pool_create (pool); /* iteration pool */
apr_dir_t *dir;
apr_finfo_t finfo;
apr_status_t apr_err;
apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
apr_err = apr_dir_open (&dir, directory, pool);
if (apr_err)
return apr_err;
/* Loop over the directory entries, clearing the subpool at the top of
each iteration. */
for (apr_err = apr_dir_read (&finfo, flags, dir);
apr_err == APR_SUCCESS;
apr_err = apr_dir_read (&finfo, flags, dir))
{
const char *child_path;
/* Clear the per-iteration SUBPOOL. */
svn_pool_clear (subpool);
/* Skip entries for "this dir" ('.') and its parent ('..'). */
if (finfo.filetype == APR_DIR)
{
if (finfo.name[0] == '.'
&& (finfo.name[1] == '\0'
|| (finfo.name[1] == '.' && finfo.name[2] == '\0')))
continue;
}
/* Build CHILD_PATH from DIRECTORY and FINFO.name. */
child_path = svn_path_join (directory, finfo.name, subpool);
/* Do some task to this encountered path. */
do_some_task (child_path, subpool);
/* Handle subdirectories by recursing into them, passing SUBPOOL
as the pool for temporary allocations. */
if (finfo.filetype == APR_DIR)
{
apr_err = crawl_dir (files, child_path, subpool);
if (apr_err)
return apr_err;
}
/* Handle files by adding their paths to the FILES array. */
else if (finfo.filetype == APR_REG)
{
/* Copy the file's path into the FILES array's pool. */
child_path = apr_pstrdup (hash_pool, child_path);
/* Add the path to the array. */
(*((const char **) apr_array_push (files))) = child_path;
}
}
/* Destroy SUBPOOL. */
svn_pool_destroy (subpool);
/* Check that the loop exited cleanly. */
if (apr_err)
return apr_err;
/* Yes, it exited cleanly, so close the dir. */
apr_err = apr_dir_close (dir);
if (apr_err)
return apr_err;
return APR_SUCCESS;
}
~~~
在前一個例子里描述了在循環和迭代情況下有效地池使用,每次迭代會從為方法傳遞一個新建的子池開始,池在循環區域中使用,在每次迭代清理。結果是內存使用比例和深度成比例,而不是頂級目錄包含所有的子目錄的總數量。當迭代的第一個調用最終結束時,實際上只有很小的傳遞過來的數據存放在池中,現在想想一下如果在每片數據使用時使用`alloc()`和`free()`時會面臨的復雜性!
池并不是對所有的應用是理想的,但是在Subversion中非常有用,作為一個Subversion開發者,你會需要學會適應池并且正確地使用它,內存使用的bug和膨脹可能會非常難于診斷和修正,但是APR提供的pool結構被證明了是非常的方便的,節約時間的功能。
- 第1章介紹
- Subversion的歷史
- Subversion的特性
- Subversion的架構
- 安裝Subversion
- Subversion的組件
- 快速入門
- 第2章基本概念
- 版本模型
- Subversion實戰
- 摘要
- 第3章指導教程
- 導入
- 修訂版本: 號碼、關鍵字和日期,噢,我的!
- 初始化的Checkout
- 基本的工作周期
- 檢驗歷史
- 其他有用的命令
- 摘要
- 第4章分支與合并
- 使用分支
- 在分支間拷貝修改
- 常見用例
- 轉換工作拷貝
- 標簽
- 分支維護
- 摘要
- 第5章版本庫管理
- 版本庫的創建和配置
- 版本庫維護
- 添加項目
- 摘要
- 第6章配置服務器
- 網絡模型
- svnserve,一個自定義的服務器
- httpd,Apache的HTTP服務器
- 支持多種版本庫訪問方法
- 第7章高級主題
- 屬性
- Peg和實施修訂版本
- 外部定義
- 賣主分支
- 本地化
- Subversion版本庫URL
- 第8章開發者信息
- 使用API
- 進入工作拷貝的管理區
- WebDAV
- 使用內存池編程
- 為Subversion做貢獻
- 第9章Subversion完全參考
- svn add
- svn blame
- svn cat
- svn checkout
- svn cleanup
- svn commit
- svn copy
- svn delete
- svn diff
- svn export
- svn help
- svn import
- svn info
- svn list
- svn log
- svn merge
- svn mkdir
- svn move
- svn propdel
- svn propedit
- svn propget
- svn proplist
- svn propset
- svn resolved
- svn revert
- svn status
- svn switch
- svn update
- svnadmin
- svnadmin create
- svnadmin deltify
- svnadmin dump
- svnadmin help
- svnadmin hotcopy
- svnadmin list-dblogs
- svnadmin list-unused-dblogs
- svnadmin load
- svnadmin lstxns
- svnadmin recover
- svnadmin rmtxns
- svnadmin setlog
- svnadmin verify
- svnlook
- svnlook author
- svnlook cat
- svnlook changed
- svnlook date
- svnlook diff
- svnlook dirs-changed
- svnlook help
- svnlook history
- svnlook info
- svnlook log
- svnlook propget
- svnlook proplist
- svnlook tree
- svnlook uuid
- svnlook youngest
- svnserve
- svnversion
- mod_dav_svn Configuration Directives
- 附錄A.Subversion對于CVS用戶
- 目錄的版本
- 更多離線操作
- 區分狀態和更新
- 分支和標簽
- 元數據屬性
- 沖突解決
- 二進制文件和轉化
- 版本化的模塊
- 認證
- 轉化CVS版本庫到Subversion
- 附錄C.WebDAV和自動版本化
- 自動版本化交互性
- Subversion和DeltaV
- 術語表