【发布时间】: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 函数中设置了断点,执行似乎没有进入它。
我想我的主要问题是:
- 是否有任何特殊原因导致这不起作用? 编辑:根据我自己的测试和这里的cmets,我认为汇编代码是问题区域。
- 更重要的是,有没有更好的方法来做我认为代码试图做的事情?
我认为像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