7

我有一个使用 SQLite3 来维护数据库文件的控制台模式程序。执行需要一段时间,但在任何时候取消都应该是安全的,假设数据库写入发生了。(这都是Windows下的)

从正在运行的程序的角度来看,CtrlC在控制台中点击是否比让另一个程序在其上调用 TerminateProcess 更安全?

我注意到,如果调用 TerminateProcess,我可能会损坏数据库——我认为这是因为程序没有机会完成写入。我的猜测是CtrlC更好,因为程序收到一个信号并自行终止,而不是操作系统杀死它。

请注意,程序实际上并不处理信号(除非 SQLite 处理);我说的是 Win32 可执行文件的内置默认机制来处理CtrlC信号。

为了澄清/简化问题 - 鉴于此写入刚刚执行:

fwrite(buf, 1024*1024, 1, stream);

在此写入期间, TerminateProcess 的行为是否会与CtrlC?

4

4 回答 4

6

所有这些都是令人信服的论点,但唯一确定的方法就是尝试一下。所以我写了一个简单的程序,分配一个 1GB 的缓冲区,分配一些数据给它,然后使用单个 fwrite() 将它写入一个文件。我尝试了几种方法来让写入“损坏”数据(具体来说,我期待的是一个截断的文件):

  • 调用 TerminateProcess(通过 perl 的 kill 函数和 Win32::Process::Kill)
  • CtrlC
  • 使用任务管理器的“结束进程”
  • 使用 Process Explorer 的“Kill Process”

没有什么能阻止写入——在任何情况下,文件的大小都是正确的,并且具有正确的数据。尽管“杀死”会立即发生,但该过程会一直持续到写入完成。

从 I/O 的角度来看, TerminateProcess 和从 I/O 的角度来看没有区别似乎是结论性CtrlC的——一旦写入开始,它似乎可以保证完成(除非断电)。

于 2009-08-04T22:02:45.497 回答
1

SQLite 声称是原子的,即使在电源故障期间,参见http://www.sqlite.org/atomiccommit.html

唯一的例外是某些磁盘系统会在数据实际写入磁盘盘片之前报告写入成功,即数据在磁盘缓存中,或者操作系统对 SQLite 撒谎。请参阅http://www.sqlite.org/lockingv3.html第 6.0 节如何损坏您的数据库文件。

被终止的进程必须停止所有正在运行的线程并在它们退出之前完成挂起的 I/O 操作。只要进程没有崩溃,就应该保证数据的完整性。

于 2009-07-25T15:56:33.930 回答
1

您的应用程序必须有机会处理CtrlCCtrlBreak,作为击键或信号(取决于配置),这意味着应用程序有机会干净退出,这些将是终止进程的更柔和的方式,允许它一点如果没有别的,更多的执行时间。

TerminateProcess 更像是一个踢,应用程序无法处理它,它来自内核,如果应用程序可以处理它,这将产生各种问题,“不可杀死”的进程挂起它们的 TerminateProcess 处理程序并拒绝出口。

据我了解,一旦在进程上调用 TerminateProcess,它就不能再执行代码了,没有清理,没有关闭,它刚刚结束,你无法处理它,如果你可以,它根本没有意义,不是从安全的角度来看。

关于处理 Windows 控制台信号的优秀代码项目文章:

http://www.codeproject.com/KB/winsdk/console_event_handling.aspx

实现上面的一些信号处理,您可以确保数据库写入有机会在程序退出之前完成,而不是让它有机会。

您可以阻止 TerminateProcess,但它并不是真正的“礼貌”编程,它更像是 root kit 编程,我在 rootkit.com 上看到过一篇很好的文章,所以一旦你拥有了无敌进程,就在那里搜索“Process Invincibility”,它可以在收到这样的“请求”后自行关闭,并在 hnad 之前执行任何清理,但这肯定是一个 hack。

在我看来,CtrlC您当前看到的行为是由于它不会立即结束正在运行的进程。

于 2009-07-27T12:27:33.660 回答
0

我相信出于您的目的,使用CtrlC. 这将导致发送到程序终止的信号。如果程序不处理信号,它将当场终止。

TerminateProcess 强制终止进程和任何子线程。

来自MSDN

TerminateProcess 函数 终止指定进程及其所有线程。... 评论

TerminateProcess 函数用于无条件地导致进程退出。如果使用 TerminateProcess 而不是 ExitProcess,则动态链接库 (DLL) 维护的全局数据的状态可能会受到影响。

TerminateProcess 启动终止并立即返回。这将停止进程中所有线程的执行,并请求取消所有挂起的 I/O。在所有待处理的 I/O 完成或取消之前,终止的进程无法退出。

进程不能阻止自己被终止。

进程有办法阻止 TerminateProcess,但我怀疑 SQLite3 会这样做。

于 2009-07-27T21:04:39.297 回答