在程序退出的【注】,我們往往不會(huì)自己關(guān)閉Event_Handler,而寄希望Reactor 的清理。但是實(shí)際情況會(huì)復(fù)雜很多。使用的時(shí)候必須當(dāng)心。
【注】是否要在退出的時(shí)候清理所有分配的內(nèi)存?在普通的操作系統(tǒng)中,程序的退出會(huì)回收所有的分配內(nèi)存。所以很多人會(huì)逃避在最后階段的清理分配的內(nèi)存。但是這實(shí)在不是一個(gè)良好的喜歡。一方面對(duì)于很多OS(比如嵌入系統(tǒng))不會(huì)回收內(nèi)存資源,一些內(nèi)核資源(UNIX)也不會(huì)在進(jìn)程退出后釋放,編程就應(yīng)該要養(yǎng)成清理的好習(xí)慣,更何況不進(jìn)行釋放在內(nèi)存檢查的軟件一般會(huì)報(bào)錯(cuò),如果不清理會(huì)干擾我們對(duì)于內(nèi)存泄露的定位。
1 Reactor的close可能不會(huì)關(guān)閉Event_Handler
理論上講,ACE_Reactor提供了一個(gè)close函數(shù),所有的Event_Handler應(yīng)該統(tǒng)一在這個(gè)函數(shù)進(jìn)行關(guān)閉。
ACE_Reactor采用的是模式,封裝了不同Reactor的實(shí)現(xiàn)。這些實(shí)現(xiàn)的close函數(shù)未存在一定的差異性。就我的閱讀和嘗試來看,Select_Reactor在close函數(shù)關(guān)閉了所有的IO句柄相關(guān)的Event_Handler,而Dev_Poll_Reactor的close實(shí)現(xiàn)就沒有關(guān)閉。
Select_Reactor的close代碼。
template int
ACE_Select_Reactor_T::close (void)
{
……
//在handler_rep的close函數(shù)會(huì)關(guān)閉所有的register的句柄的handler,調(diào)用他們的
//handle_close函數(shù)
this->handler_rep_.close ();
Dev_Poll_Reactor的close的調(diào)用了函數(shù)ACE_Dev_Poll_Reactor_Handler_Repository::close,而后有逐步調(diào)用了unbind_all,remove_reference。
//close會(huì)經(jīng)過多級(jí)調(diào)用到ACE_Dev_Poll_Reactor_Handler_Repository:: unbind_all
//unbind被unbind_all函數(shù)調(diào)用decr_refcnt == true
int
ACE_Dev_Poll_Reactor_Handler_Repository::unbind (ACE_HANDLE handle,
bool decr_refcnt)
{……
// remove_reference函數(shù)沒有調(diào)用handle_close,而是減去了引用計(jì)數(shù)
if (decr_refcnt)
this->handlers_[handle].event_handler->remove_reference ();
……
}
ACE_Event_Handler::Reference_Count
ACE_Event_Handler::remove_reference (void)
{
//如果打開了引用計(jì)數(shù),則使用應(yīng)用計(jì)數(shù)方式管理方式。但是代碼默認(rèn)不采用應(yīng)用計(jì)數(shù)模式
//所以下面的代碼都無法執(zhí)行
if (reference_counting_required)
{
//減去引用計(jì)數(shù)
Reference_Count result =
--this->reference_count_;
//如果已經(jīng)沒用引用個(gè)數(shù)了,刪除自己。
if (result == 0)
delete this;
}
可以看到ACE_Event_Handler的代碼默認(rèn)不采用應(yīng)用計(jì)數(shù)模式,(eference_counting_required默認(rèn)為DISABLED)而Dev_Poll_Reactor卻非要使用引用計(jì)數(shù)模式去清理Event_Handler。
我對(duì)Dev_Poll_Reactor為什么要設(shè)計(jì)成這樣表示不解。也對(duì)Dev_Poll_Reactor提交過BUG,但是Dev_Poll_Reactor的開發(fā)者不認(rèn)為這樣有什么不妥,本人E文羞澀,無法說服具體的開發(fā)人員,不過在提交BUG時(shí),居然得到了Douglas反饋(他開始時(shí)認(rèn)同我的看法),對(duì)于他們的執(zhí)著和認(rèn)真還是表示敬仰。
2 可能會(huì)導(dǎo)致重復(fù)釋放引發(fā)Coredump
這個(gè)問題是在工作中調(diào)試一個(gè)BUG出現(xiàn)的。
在測(cè)試一個(gè)服務(wù)器的時(shí)候發(fā)現(xiàn)Coredump發(fā)生kill進(jìn)程,讓其退出在之后,會(huì)出現(xiàn)Coredump文件。Coredump顯示出現(xiàn)問題的地方在。
#1 0x0805bc7b in ~ACE_Timer_Heap_T (this=0x82d3ec8) at /usr/local/ACE_wrappers/ace/Timer_Queue_T.cpp:442
#2 0x0805b86d in ~ACE_Singleton (this=0x82cca70) at egg_application.cpp:52
#3 0x08056785 in ACE_Singleton::cleanup (this=0x82dfb90)
由于希望改變ACE_Time_Queue的特性(數(shù)量),我替換Reactor的默認(rèn)Time_Queue,所以必須自己銷毀自己管理的TimeQueue。而在外部最后銷毀的時(shí)候出現(xiàn)Coredump。由于和Time_Queue相關(guān),我檢查了所有的Timer相關(guān)的Event_handler,發(fā)現(xiàn)有一個(gè)Event_handler沒有自己主動(dòng)調(diào)用handler_close釋放,這個(gè)Event_handler只有定時(shí)器,沒有注冊(cè)任何IO事件。修改代碼為主動(dòng)釋放后,再次測(cè)試就發(fā)現(xiàn)Coredump的問題得到解決。
檢查了一下原有代碼堆棧的調(diào)用順序,找到了問題原因。
(1)ACE_Reactor::close,實(shí)際調(diào)用ACE_Select_Reactor::close
(2) Select_Reactor::close 嘗試關(guān)閉所有的IO句柄相關(guān)的Event_handler,但由于Time_Queue是外部傳入的參數(shù),所以不清理Time_Queue。
(3)Time_Queue清理,Time_Queue的析構(gòu)函數(shù)被調(diào)用,Time_Queue的析構(gòu)函數(shù)會(huì)釋放所有的定時(shí)器相關(guān)的Event_handler。而他的釋放還會(huì)調(diào)用hanlder_close。但是這是Reactor對(duì)象已經(jīng)銷毀了。所以造成了Coredump。
注意由于Reactor的封裝了Event_handler定時(shí)器,IO句柄,Notify機(jī)制等回調(diào)接口。所以Event_handler可能只關(guān)聯(lián)到IO句柄,也可能只關(guān)聯(lián)定時(shí)器,同時(shí)Reactor的模型決定了他的內(nèi)部管理是復(fù)雜的。而在釋放的過程中很可能會(huì)發(fā)生交錯(cuò)的問題,而,像上面問題的Event_handler就只關(guān)聯(lián)的定時(shí)器,所以在Reactor的close的時(shí)候沒有關(guān)閉。從而導(dǎo)致在后面的清理工作中產(chǎn)生時(shí)序問題。
最簡(jiǎn)單的方式還是自己在程序退出前清理釋放所有的Event_handler.再調(diào)用Reactor的close。
【注】是否要在退出的時(shí)候清理所有分配的內(nèi)存?在普通的操作系統(tǒng)中,程序的退出會(huì)回收所有的分配內(nèi)存。所以很多人會(huì)逃避在最后階段的清理分配的內(nèi)存。但是這實(shí)在不是一個(gè)良好的喜歡。一方面對(duì)于很多OS(比如嵌入系統(tǒng))不會(huì)回收內(nèi)存資源,一些內(nèi)核資源(UNIX)也不會(huì)在進(jìn)程退出后釋放,編程就應(yīng)該要養(yǎng)成清理的好習(xí)慣,更何況不進(jìn)行釋放在內(nèi)存檢查的軟件一般會(huì)報(bào)錯(cuò),如果不清理會(huì)干擾我們對(duì)于內(nèi)存泄露的定位。
1 Reactor的close可能不會(huì)關(guān)閉Event_Handler
理論上講,ACE_Reactor提供了一個(gè)close函數(shù),所有的Event_Handler應(yīng)該統(tǒng)一在這個(gè)函數(shù)進(jìn)行關(guān)閉。
ACE_Reactor采用的是模式,封裝了不同Reactor的實(shí)現(xiàn)。這些實(shí)現(xiàn)的close函數(shù)未存在一定的差異性。就我的閱讀和嘗試來看,Select_Reactor在close函數(shù)關(guān)閉了所有的IO句柄相關(guān)的Event_Handler,而Dev_Poll_Reactor的close實(shí)現(xiàn)就沒有關(guān)閉。
Select_Reactor的close代碼。
template
ACE_Select_Reactor_T::close (void)
{
……
//在handler_rep的close函數(shù)會(huì)關(guān)閉所有的register的句柄的handler,調(diào)用他們的
//handle_close函數(shù)
this->handler_rep_.close ();
Dev_Poll_Reactor的close的調(diào)用了函數(shù)ACE_Dev_Poll_Reactor_Handler_Repository::close,而后有逐步調(diào)用了unbind_all,remove_reference。
//close會(huì)經(jīng)過多級(jí)調(diào)用到ACE_Dev_Poll_Reactor_Handler_Repository:: unbind_all
//unbind被unbind_all函數(shù)調(diào)用decr_refcnt == true
int
ACE_Dev_Poll_Reactor_Handler_Repository::unbind (ACE_HANDLE handle,
bool decr_refcnt)
{……
// remove_reference函數(shù)沒有調(diào)用handle_close,而是減去了引用計(jì)數(shù)
if (decr_refcnt)
this->handlers_[handle].event_handler->remove_reference ();
……
}
ACE_Event_Handler::Reference_Count
ACE_Event_Handler::remove_reference (void)
{
//如果打開了引用計(jì)數(shù),則使用應(yīng)用計(jì)數(shù)方式管理方式。但是代碼默認(rèn)不采用應(yīng)用計(jì)數(shù)模式
//所以下面的代碼都無法執(zhí)行
if (reference_counting_required)
{
//減去引用計(jì)數(shù)
Reference_Count result =
--this->reference_count_;
//如果已經(jīng)沒用引用個(gè)數(shù)了,刪除自己。
if (result == 0)
delete this;
}
可以看到ACE_Event_Handler的代碼默認(rèn)不采用應(yīng)用計(jì)數(shù)模式,(eference_counting_required默認(rèn)為DISABLED)而Dev_Poll_Reactor卻非要使用引用計(jì)數(shù)模式去清理Event_Handler。
我對(duì)Dev_Poll_Reactor為什么要設(shè)計(jì)成這樣表示不解。也對(duì)Dev_Poll_Reactor提交過BUG,但是Dev_Poll_Reactor的開發(fā)者不認(rèn)為這樣有什么不妥,本人E文羞澀,無法說服具體的開發(fā)人員,不過在提交BUG時(shí),居然得到了Douglas反饋(他開始時(shí)認(rèn)同我的看法),對(duì)于他們的執(zhí)著和認(rèn)真還是表示敬仰。
2 可能會(huì)導(dǎo)致重復(fù)釋放引發(fā)Coredump
這個(gè)問題是在工作中調(diào)試一個(gè)BUG出現(xiàn)的。
在測(cè)試一個(gè)服務(wù)器的時(shí)候發(fā)現(xiàn)Coredump發(fā)生kill進(jìn)程,讓其退出在之后,會(huì)出現(xiàn)Coredump文件。Coredump顯示出現(xiàn)問題的地方在。
#1 0x0805bc7b in ~ACE_Timer_Heap_T (this=0x82d3ec8) at /usr/local/ACE_wrappers/ace/Timer_Queue_T.cpp:442
#2 0x0805b86d in ~ACE_Singleton (this=0x82cca70) at egg_application.cpp:52
#3 0x08056785 in ACE_Singleton
由于希望改變ACE_Time_Queue的特性(數(shù)量),我替換Reactor的默認(rèn)Time_Queue,所以必須自己銷毀自己管理的TimeQueue。而在外部最后銷毀的時(shí)候出現(xiàn)Coredump。由于和Time_Queue相關(guān),我檢查了所有的Timer相關(guān)的Event_handler,發(fā)現(xiàn)有一個(gè)Event_handler沒有自己主動(dòng)調(diào)用handler_close釋放,這個(gè)Event_handler只有定時(shí)器,沒有注冊(cè)任何IO事件。修改代碼為主動(dòng)釋放后,再次測(cè)試就發(fā)現(xiàn)Coredump的問題得到解決。
檢查了一下原有代碼堆棧的調(diào)用順序,找到了問題原因。
(1)ACE_Reactor::close,實(shí)際調(diào)用ACE_Select_Reactor::close
(2) Select_Reactor::close 嘗試關(guān)閉所有的IO句柄相關(guān)的Event_handler,但由于Time_Queue是外部傳入的參數(shù),所以不清理Time_Queue。
(3)Time_Queue清理,Time_Queue的析構(gòu)函數(shù)被調(diào)用,Time_Queue的析構(gòu)函數(shù)會(huì)釋放所有的定時(shí)器相關(guān)的Event_handler。而他的釋放還會(huì)調(diào)用hanlder_close。但是這是Reactor對(duì)象已經(jīng)銷毀了。所以造成了Coredump。
注意由于Reactor的封裝了Event_handler定時(shí)器,IO句柄,Notify機(jī)制等回調(diào)接口。所以Event_handler可能只關(guān)聯(lián)到IO句柄,也可能只關(guān)聯(lián)定時(shí)器,同時(shí)Reactor的模型決定了他的內(nèi)部管理是復(fù)雜的。而在釋放的過程中很可能會(huì)發(fā)生交錯(cuò)的問題,而,像上面問題的Event_handler就只關(guān)聯(lián)的定時(shí)器,所以在Reactor的close的時(shí)候沒有關(guān)閉。從而導(dǎo)致在后面的清理工作中產(chǎn)生時(shí)序問題。
最簡(jiǎn)單的方式還是自己在程序退出前清理釋放所有的Event_handler.再調(diào)用Reactor的close。