结束处理程序——Windows核心编程学习手札之二十三
結束處理程序
——Windows核心編程學習手札之二十三
使用SEH可以只關注程序要完成任務,而運行中發生的錯誤,系統將會發現并通知。Windows引入SHE是為了便于操作系統的開發,使用SHE所造成的負擔主要由編譯程序來承擔,而不是由操作系統承擔。當異常塊(exception block)出現時,編譯程序要生成特殊的代碼,產生一些表(table)來支持處理SHE的數據結構,提供回調(callback)函數,操作系統可以調用這些函數,保證異常塊被處理。編譯程序還負責準備棧結構和其他內部信息,供操作系統使用和參考。在編譯程序中增加SHE支持,不同的編譯商會以不同的方式實現SEH。SHE實際包含兩個主要功能:結束處理(termination handling)和異常處理(exception handling)。
一個結束處理程序能夠確保去調用和執行一個代碼塊(結束處理程序,termination handling),而不管另外一段代碼(保護體,guarded body)是如何退出,結束處理程序的文法結構如下:
?????? __try{
???????????????????? //guarded body
??????????? 。。。
???????? }
?????? __finally{
????????????? ?? //termination handler
?????????? 。。。
?????? }
__try和__finally關鍵字用來標出處理程序兩段代碼的輪廓,操作系統和編譯程序共同確保結束處理程序中的__finally代碼塊能夠被執行,不管保護體(try塊)是如何退出的,不論保護體中使用return,還是goto,或者是longjump,結束處理程序(finally塊)都將被調用。
盡管結束處理程序可以捕捉try塊過早退出的大多數情況,但當線程或進程被結束時,卻不能引起finally塊中的代碼執行。當調用ExitThread或ExitProcess時,將立即結束線程或進程,而不會執行finally塊中的任何代碼,另外,由于某個程序調用terminateThread或terminateProcess,線程或進程將死掉,finally塊中的代碼也不執行。某些C運行期函數(如abort)要調用ExitProcess,也使finally塊中的代碼不能執行,雖然沒有辦法阻止其他程序結束線程或進程,但可以避免過早調用ExitThread和ExitProcess,以執行finally塊中代碼。最好將return、continue、break、goto等語句從結束處理程序的try塊和finally塊中移出去,放在結束處理程序的外面,這樣使編譯程序產生較小的代碼,因為不需要再捕捉try塊中的過早退出,也使編譯程序產生更快的代碼(因為執行局部展開的指令也不少),另外代碼更容易閱讀和維護。
為避免在try塊中使用return語句,microsoft在C/C++編譯程序中增加了__leave關鍵字。在try塊中使用__leave關鍵字會引起跳轉到try塊的結尾,即跳轉到try塊的右大括號,由于控制流自然地從try塊中退出并進入finally塊,不產生系統開銷,不過需要增加BOOL型變量來指示函數的成功或失敗,當然這個代價是很小的。按照這個方式利用結束處理程序來設計函數時,需要在進入try塊之前,將所有資源句柄初始化為無效的值,然后在finally塊中查看那些資源被成功的分配,就可以知道那些要釋放。另外一種確定需要釋放資源的辦法是對成功分配的資源設置一個標志,然后在finally塊中的代碼檢查標志的狀態,來確定資源是否被釋放。
區分強制執行finally塊的情況:
1)從try塊中進入finally塊的正常控制流;
2)局部展開:從try塊中過早退出(goto、longjump、contiune、break、return等)強制控制轉移到finally塊,由于系統開銷比較大,盡量避免在try塊中過早退出,可使用__leave代替return;
3)全局展開(global unwind),發生時沒有明顯標識,如引起一個內存訪問違規(memory access violation),一個全局展就會在finally塊執行。
為了確定是那種情況引起finally塊執行,可調用內部函數(或內蘊函數,intrinsic function)Abnormal Termination:
?????? BOOL AbnormalTermination();
這個內部函數只在finally塊中調用,返回一個Boolean值,指出與finally塊相結合的try塊是否過早退出,如果控制流離開try塊并自然進入finally塊,則返回FALSE;如果控制流非正常退出try塊(由于goto/return/break/contiune語句引起的局部展開),或由于內部訪問違規或其他異常引起的全局展開,將返回TRUE,這里沒發區別finally塊的執行是由于局部展開還是全局展開。
內部函數是編譯程序識別的一種特殊函數,編譯程序為內部函數產生內聯(inline)代碼而不是生成調用函數的代碼,例如,memcpy是一個內部函數(如果指定/Oi編譯程序開關)當編譯程序看到一個對memcpy的調用,直接將memcpy的代碼插入調用memcpy的 函數中,而不是生成一個對memcpy函數的調用,其作用是代碼的長度增加了,但執行速度加快了。
利用結束處理程序的理由:
1)簡單錯誤處理,所有清理工作在一個位置并且保證被執行;
2)提高程序的可讀性;
3)使代碼更加容易維護;
4)如果使用得當,具有最小的系統開銷。
?
總結
以上是生活随笔為你收集整理的结束处理程序——Windows核心编程学习手札之二十三的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 插入DLL和挂接API——Windows
- 下一篇: 异常处理程序和软件异常——Windows