【问题标题】:Is there a better way to implement this memory management?有没有更好的方法来实现这种内存管理?
【发布时间】:2012-01-11 05:39:38
【问题描述】:

这是我之前的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:
    }
}

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

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

我想我的主要问题是:

  1. 是否有任何特殊原因导致这不起作用? 编辑:根据我自己的测试和这里的cmets,我认为汇编代码是问题区域。
  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 声明。然而,我希望避免这种情况,因为它在很多地方都存在。

【问题讨论】:

  • 处理程序的安装看起来很可疑。无论如何,msdn.microsoft.com/en-us/library/windows/desktop/… 有一个例子
  • 我想最初的意图是做与__try, __except 相同的事情,而实际上并没有在代码中添加大量这些子句。这很有帮助,谢谢!
  • 这段代码的想法似乎与我的here 相似,只是你的代码不太清楚。这种方法的问题是需要非常小心地实现 try、__try 和异常过滤器,以免吞下或禁用其他人的异常或破坏程序状态。

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


【解决方案1】:

如果没有看到更多代码,我不能 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 内部出现异常,将首先调用 function 中的处理程序,如果该处理程序没有处理它,则会调用 SetHandler 安装的处理程序。

【讨论】:

  • 我认为我因为遗漏了一些相关代码而产生了误导。我已经更新了问题。
猜你喜欢
  • 2021-11-15
  • 2011-10-23
  • 1970-01-01
  • 2020-04-05
  • 1970-01-01
  • 2013-08-04
  • 2023-03-31
  • 1970-01-01
  • 2019-08-19
相关资源
最近更新 更多