有没有更好的方法来实现这种内存管理?

时间:2011-12-02 05:30:37

标签: c++ visual-c++ memory-management assembly

这是我之前question regarding exceptions的后续内容。

我有一些我试图维护的遗留代码。它有一个我很难理解的自定义内存管理组件。

我对该系统的理解如下:

调用函数要求为其分配一些内存,提供所需的初始内存量(needed)和最大量(max)。这叫:

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

我理解保留内存但不提供访问权限。换句话说,如果我尝试写入保留段,我将获得访问冲突。

然后调用:

VirtualAlloc(base, needed, MEM_COMMIT, PAGE_READWRITE);

这使needed内存量从base开始。

当试图检测何时需要访问更多内存时,会出现粘性部分。我的理解是,系统会在发生访问冲突异常时尝试捕获它们,并在地址上调用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:
    }
}

通过在方法范围中实例化特定类来调用此方法。看起来它只将处理程序应用于当前堆栈上下文;如果异常未传播到当前方法,则当前方法不会处理在被调用函数中抛出的异常。

所有这一切的结果是不仅没有捕获访问冲突,而且它在堆栈的当前顶部禁用异常处理。我在exceptionCatch函数中设置了断点,执行似乎没有输入。

我想我的主要问题是:

  1. 为什么这不起作用有什么特别的原因吗? 编辑:根据我自己的测试和评论,我认为汇编代码是问题所在。
  2. 更重要的是,有没有更好的方法来做我认为代码试图做的事情?
  3. 我不认为像set_unexpected这样的东西是可行的,因为内存管理只适用于这个特定的库,而客户端应用程序可能(在我们的例子中)有自己的意外异常处理程序。 / p>

    修改

    通过使用以下类构造函数和析构函数声明类bExceptionRegistration来完成每个堆栈的处理程序的设置和取消设置:

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

    因此,要实际设置特定堆栈范围的处理程序,您将拥有:

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

    编辑:我猜这可能是最合适的解决办法,就是用bExceptionRegistration块替换代码中的__try, __except声明。我希望避免这种情况,因为它存在于很多地方。

1 个答案:

答案 0 :(得分: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块。

因此,如果在function3中有异常,则首先调用函数中的处理程序,如果该处理程序不处理它,将调用由SetHandler安装的处理程序。