检测程序终止(C,Windows)

9
我有一个程序,在完成前必须执行某些任务。问题是,有时程序会因为异常(比如无法连接到数据库等)而崩溃。 现在,有没有办法检测到异常终止并在程序死亡之前执行一些代码呢?
谢谢。
欢迎提供代码。

使用一个SIGSEGV信号处理程序。;-) - Dave Jarvis
7个回答

9

1. Win32

Win32 API包含一种通过SetUnhandledExceptionFilter函数来实现此功能的方法,具体如下:

LONG myFunc(LPEXCEPTION_POINTERS p)
{
     printf("Exception!!!\n");     
     return EXCEPTION_EXECUTE_HANDLER;
}

int main()
{
     SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)&myFunc);    
     // generate an exception !
     int x = 0;
     int y = 1/x;
     return 0;
}

2. POSIX/Linux

通常我会通过signal()函数来处理SIGSEGV信号。你也可以处理SIGTERM信号和SIGINT信号,但不能处理SIGKILL信号(这是设计如此)。你可以使用strace()来获取回溯信息以查看信号的原因。


4
关于你对TerminatProcess()的评论,简单的回答是:在调用TerminateProcess()后,你无法进行任何操作。不能通过任何方式继续进程,也不能获得任何资源。该进程立即结束,没有任何可能性再次启动。 - cigarman
感谢您的评论。问题不在于异常,而在于检测崩溃或终止调用。 异常我已经可以处理了。 - wonderer
这就是SetUnhandledExceptionFilter的作用,它处理像堆栈溢出、空内存访问等“崩溃”情况。它不仅仅是try/catch语句中的异常处理程序。 - cigarman
请参阅http://msdn.microsoft.com/en-us/library/aa363082(VS.85).aspx,其中详细说明了SEH可以处理的所有“崩溃”情况。 - cigarman
此外,我认为函数名称让您感到困惑。这个函数专门用于处理您所称的“异常终止”。换句话说,当您的代码崩溃时,就会调用此函数。它不能处理TerminateProcess,这是由Piotr指出的,因此如果您的进程通过TerminateProcess终止,它将不会“触发”,这是有意设计的。在崩溃事件中,无数程序使用SEH来“联络家人”。 - cigarman
谢谢Coleman,你的评论说无法推迟TerminatProcess调用,除非通过hooking的方式。这正是我一直在寻找的答案。 - Stefan Lundström

5

sysinternals论坛帖子讨论如何通过挂钩NT内部来防止终止进程的尝试,但你真正需要的是看门狗或同级进程(合理的方法),或者拦截灾难性事件的一些方法(相当冒险)。

编辑:他们有很多原因让这个过程变得困难,但是拦截或阻止杀死进程的尝试是可能的。我知道你只是想在退出之前清理一下,但是一旦有人释放了一个无法立即终止的进程,就会有人要求立即终止它的方法,依此类推。总之,如果要走这条路,请参见上面链接的线程,并搜索其中的一些关键词以获取更多信息,例如hook OR filter NtTerminateProcess等。我们在这里谈论内核代码、设备驱动程序、反病毒软件、安全、恶意软件、rootkit等内容。在这个领域帮助你的一些书籍是Windows NT/2000 Native APIUndocumented Windows 2000 Secrets: A Programmer's CookbookRootkits: Subverting the Windows Kernel,当然还有Windows® Internals: Fifth Edition。这些东西编码不太难,但是非常敏感,你可能会引入意外的副作用。

也许 应用程序恢复和重启功能 可以派上用场?支持 Vista 和 Server 2008 及以上版本。

ApplicationRecoveryCallback 回调函数 应用程序定义的回调函数,用于在应用程序遇到未处理的异常或变得无响应时保存数据和应用程序状态信息。

在使用 SetUnhandledExceptionFilter 时,MSDN Social 讨论 建议为了可靠地使其工作,唯一的方法是在内存中打补丁来确保您的过滤器得到调用。建议改用 __try/__except 进行封装。无论如何,在文章 "SetUnhandledExceptionFilter" and VC8 中有关于过滤对 SetUnhandledExceptionFilter 的调用的示例代码和讨论。

此外,请参阅The Awesome Factor的Windows SEH Revisited,其中包含一些使用AddVectoredExceptionHandler的示例代码。

谢谢。我之前看过那些内容。不过有两个问题。1)我需要支持XP。2)我不需要从崩溃中恢复。我只需要在关闭应用程序之前(无论是正常关闭还是崩溃前),保存几个文件并关闭数据库连接。 - wonderer
明白了。添加了一些信息和示例代码以及有关SUEF和AVEH的讨论链接。 - b w
让我试试这个。但是,我无法捕获TerminateProcess。从我所看到的来看,这似乎不可能。 - wonderer
我相信这是可能的,只是需要大量的工作。在内核级别,你可以做任何事情,钩子和修补几乎所有东西。搜索“hook ntterminateprocess”等。我会稍微扩展一下我的第一段,但请记住,他们让它变得困难,因为他们不想要一场军备竞赛。也就是说,下一个问题是“如何杀死一个拒绝立即终止的错误进程?”我们在设备驱动程序、防病毒和 rootkit 领域。 - b w
你好,我不认为你是这样的人。像SUEF、AVEH、信号处理程序、看门狗进程或__try/__except等其他方法是否已经满足了你所需求的所有情况?如果是,那么这就是正确的方式(请报告哪种方法适用于你——这是一个有趣的话题)。如果你需要更多帮助,你将不得不使用一些非典型的技术。 - b w

2

取决于你如何处理异常。如果你正确地处理并退出程序,你可以使用 atexit() 注册你的函数以在退出时调用。

在真正的异常终止(如段错误)的情况下,它不起作用。

我不知道 Windows 是否支持,但在符合 POSIX 标准的操作系统上,您可以安装信号处理器来捕获不同的信号并对其进行处理。当然,你无法捕获 SIGKILLSIGSTOP 信号。

信号 API 是自 C89 以来 ANSI C 的一部分,所以很可能 Windows 支持它。有关详细信息,请查看 signal() 系统调用。


我正在使用类似的方法来捕获ctrl-c和其他终止信号,但我无法捕获TerminateProcess。 如果我没记错的话,你无法停止SIGKILL,TerminateProcess也是一样。它只会导致应用程序崩溃。 - wonderer

1

我认为这可能会有所帮助。你有一个示例代码片段吗? - wonderer
http://codepad.org/mUAUB25D - 但这只是最简单的一个,网络上还有更复杂的崩溃处理程序(例如在CodeProject上)。 - Cat Plus Plus
我尝试了,看起来很好,但是如果我杀死了进程(从任务管理器等),它就无法检测到。 - wonderer
2
来自MSDN: "如果一个进程被TerminateProcess终止,那么该进程的所有线程将立即终止,没有机会运行额外的代码。这意味着线程不会在终止处理程序块中执行代码。此外,没有附加的DLL被通知进程正在分离。" - Cat Plus Plus

0

谢谢,但这并没有真正帮助到我。我可以捕获普通的终止。 - wonderer

0

首先,尽管这很明显:你永远无法拥有完全健壮的解决方案 - 某人总是可以只需击中电源电缆来终止您的进程。因此,您需要达成妥协,并仔细制定妥协的细节。

其中一个更健壮的解决方案是将相关代码放入包装程序中。包装程序调用您的“真实”程序,等待其进程终止,然后 - 除非您的“真实”程序特别发出已正常完成的信号 - 运行清理代码。这在诸如测试套件之类的东西中非常常见,测试程序可能会以意外的方式崩溃、中止或其他死亡。

这仍然会给您带来困难,即如果某人对您的包装函数执行 TerminateProcess,那么该怎么办。如果必要,您可以将其设置为 Windows 中的服务,并使用操作系统的功能在其死亡时重新启动它。(这只是稍微改变了一些事情;某人仍然可以停止服务。)此时,您可能已经到达需要通过创建文件等持久性信号来标志成功完成的阶段。


我同意你的看法。我的主要问题是在 TerminateProcess 或其他意外事件发生之前执行最后一个操作。 其余的事件都已经处理好了。 我和负责该项目的人谈过将监控应用程序转换为服务的事情,但是被拒绝了。 我想这似乎是无法实现的... - wonderer

0
我几年前在ddj.com发表了一篇关于“死后调试”(post mortem debugging)的文章。
它包括用于检测异常终止的Windows和Unix/Linux源代码。但是根据我的经验,使用SetUnhandledExceptionFilter安装的Windows处理器并不总是被调用。在许多情况下,它会被调用,但是我从客户那里收到了相当多的日志文件,其中没有包括已安装处理器的报告,例如ACCESS VIOLATION是原因之一。

http://www.ddj.com/development-tools/185300443


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接