2011-12-02 65 views
2

這是我之前的question regarding exceptions的後續行動。有沒有更好的方法來實現這種內存管理?

我有一些遺留代碼,我試圖維護。它有一個我很難理解的自定義內存管理組件。

我的對系統的理解如下:

調用功能要求一個將要分配給它的一些存儲器,提供所需的存儲器中的初始量(needed),和最大量(max)。這就要求:

base = VirtualAlloc(0, max, MEM_RESERVE, PAGE_NOACCESS); 

我的理解保留內存,但不提供訪問。換句話說,如果我嘗試寫入保留段,我會得到訪問衝突。

然後調用:

VirtualAlloc(base, needed, MEM_COMMIT, PAGE_READWRITE); 

這使得起始於base訪問needed的內存量。

當試圖檢測何時需要訪問更多內存時,粘滯部分會出現。我的理解是系統嘗試在發生訪問衝突異常時捕獲並在地址上調用VirtualAlloc以使內存可訪問。

它通過聲明以下方法不這樣:

unsigned long __cdecl 
exceptionCatch(struct _EXCEPTION_RECORD* er, void*, struct _CONTEXT* cr, void*) 
{ 
    if(er->ExceptionCode == EXCEPTION_ACCESS_VIOLATION 
     && ExtendBuffer((void*)er->ExceptionInformation[1])) 
     return ExceptionContinueExecution; 

    return ExceptionContinueSearch; 
} 

然後,它註冊以此作爲堆棧(我認爲)的頂部的異常處理程序,使用該特別可怕一段代碼:

void __cdecl SetHandler(bExceptionRegistration& v) 
{ 
    __asm 
    { 
     mov  eax, 8[ebp]  ; get exception register record to install 
     mov  ecx, fs:[0]  ; get current head of chain 

     cmp  ecx, eax  ; should we be at head? 
     jb  search 
     mov  [eax], ecx  ; save current head 
     mov  fs:[0], eax  ; install new record at head 
     jmp  short ret1 
search: 
     cmp  [ecx], eax  ; at proper location yet? 
     ja  link 
     mov  ecx, [ecx]  ; get next link 
     jmp  search 
link: 
     mov  edx, [ecx] 
     mov  [eax], edx  ; point to next 
     mov  [ecx], eax 
ret1: 
    } 
} 

通過實例化方法範圍中的特定類來調用此方法。它看起來只是將處理程序應用於當前的堆棧上下文;和in一樣,如果異常不會傳播到當前方法,則在當前方法中不會處理被調用函數中拋出的異常。

所有這些的結果是,不僅沒有捕獲到訪問衝突,而且它禁用了當前堆棧頂部的異常處理。我在exceptionCatch函數中設置了斷點,並且執行看起來不會進入它。

我想我的主要問題是:

  1. 有什麼特別的原因,爲什麼這不應該工作? 編輯:根據我自己的測試和評論在這裏,我認爲彙編代碼是問題領域。
  2. 更重要的是,有沒有更好的方式來做我認爲代碼試圖做的事情?

我不認爲像set_unexpected是可行的,因爲內存管理只適用於這個特定的庫和客戶端應用程序可以(在我們的情況下,確實)有自己的意外的異常處理程序。

編輯:

設置和每個堆棧的處理程序不落的是通過聲明一個類bExceptionRegistration下面的類構造函數和析構函數來完成:

bExceptionRegistration :: bExceptionRegistration() : function(exceptionCatch) 
{ 
    SetHandler(*this); 
} 

bExceptionRegistration :: ~bExceptionRegistration() 
{ 
    UnsetHandler(*this); 
} 

因此,實際設置處理對於特定的堆棧範圍,您將擁有:

void someFunction() 
{ 
    bExceptionRegistration er; 
    // do some stuff here 
} 

編輯:我猜可能是所有這些最合適的解決方案是用代碼__try, __except塊代替bExceptionRegistration聲明。然而,我希望避免這種情況,因爲它在很多地方。

+1

處理程序的安裝看起來很可疑。無論如何,有一個例子在http://msdn.microsoft.com/en-us/library/windows/desktop/aa366803%28v=vs.85%29.aspx –

+0

我想最初的意圖是做同樣的事情'__try,__except'實際上並沒有用很多這些子句點代碼。這很有幫助,謝謝! – tvStatic

+1

這段代碼的想法似乎與我的[這裏](http://stackoverflow.com/q/8004945/968261)類似,只是你的代碼不太清楚。這種方法的問題是,需要非常小心地執行try,__try和異常過濾器,以免吞下或禁用其他人的異常或損壞程序狀態。 –

回答

2

我不是100%確定無法看到更多的代碼。 它沒有在堆棧頂部註冊異常處理程序,但它使用一個技巧來插入定義了EXCEPTION_REGISTRATION結構的異常處理。因此,例如,(也許在你的情況下,它實現了一個有點不同):

void function3(EXCEPTION_REGISTRATION& handler) 
{ 
    SetHandler(handler); 
    //Do other stuff 
} 
void function2(EXCEPTION_REGISTRATION& handler) 
{ 
    __try 
    { 
     //Do something 
     function3(handler); 
    } 
    __except(expression) 
    { 
     //... 
    } 
} 

void function() 
{ 
    EXCEPTION_REGISTRATION handler; 
    //..Init handler 
    function2(handler) 
} 

當你調用SetHandler它會插入異常處理,就像是在它的功能範圍。因此,在這種情況下,當您調用SetHandler時,它將顯示爲在函數中存在__try __except塊。

因此,如果函數3中有一個異常,函數中的處理程序將首先被調用,如果該處理程序不處理它,將調用由SetHandler安裝的處理程序。

+0

我想我誤導了一些相關的代碼。我已經更新了這個問題。 – tvStatic

相關問題