【问题标题】:Process.HasExited race conditionProcess.HasExited 竞争条件
【发布时间】:2015-10-29 06:39:58
【问题描述】:

我有一个System.Diagnostics.Process 的实例,它是通过Process.GetProcessesByName 创建的。

在我成功打开进程后,我会执行各种操作,例如读取它的内存和窗口标题。

这些操作是基于计时器不断执行的,我的意思是Timer.Elapsed事件处理程序是流程操作的来源。

现在,我注意到我有一个竞争条件,我无法使用我知道的任何东西来解决。它是这样发生的:

timerElapsedEvent(...) {

    if (!process.HasExited) {

        process.Refresh(); // Update title.
        var title = process.MainWindowTitle;
    }

}

如果进程正在运行,并且我的代码进入了if 块,那么进程在执行process.MainWindowTitle 调用之前退出的可能性很小,这会导致异常。

我需要一种方法来以某种方式捕获进程的退出事件并使其保持活动状态,直到可以安全地关闭它而不会使正在监视它的应用程序崩溃,从而确保它会等待@987654327 @ 关闭之前(或任何其他可以解决此问题的解决方案)。

此外,同时,另一个方法可能正在运行ReadProcessMemory,它也会崩溃。

我该如何解决这个问题?

PS:Process.Exit 事件处理程序不起作用,因为它不会在process.MainWindowTitle 之前触发,它只会在当前指令完成后触发。 我很确定以某种方式控制退出事件是解决此问题的唯一方法,因为 HasExit 可能随时更改,与在进程上实际调用方法之前我进行了多少检查无关。

PS2:我刚刚意识到这是一个 TOCTTOU 案例,除非我可以控制我打开的进程,否则这是无法解决的,所以我把它留在这里只是想看看是否有人知道这样做的方法。

【问题讨论】:

  • 简单地捕获InvalidOperationException 并将其视为退出进程有什么问题?在处理外部进程时,存在各种根本无法解决的竞态条件;您的代码只需要准备好处理可能发生的错误。
  • 嘿,彼得!捕获异常没有什么问题,我只是想确保没有办法同步线程或其他东西,没有解决方案。
  • 无论你做什么(在合理范围内......如果你准备好对系统进行rootkit并接管Windows内核,我想你可以做任何你想做的事情:)),这个过程可以在没有任何同步的可能性的情况下被杀死。正如一个人必须随时准备好捕捉FileNotFoundException,即使在刚刚看到File.Exists()返回true之后,您自己的代码也需要始终准备好让进程退出,即使Process.HasExited属性刚刚返回@ 987654335@.
  • 我 100% 同意你的推理。 Process.HasExited 会返回 false,而不是 true 8P
  • 我认为这不是线程问题。一定只是本机调用失败(或者我错过了处理此问题的托管代码中的某些内容,尽管我很确定我没有)。我做了一个快速测试并确认即使EnableRaisingEvents 未设置true 也会发生异常。所以,是的,你有 TOCTTOU,除了准备捕捉异常之外,你真的无能为力。就像我之前提到的,由于操作系统总是可以直接杀死进程,即使你可以接管进程并控制它,你仍然无法确保不会出现异常。

标签: c# process synchronization race-condition


【解决方案1】:

短版:你不能。

这里有一个基本的“检查时间到使用时间”问题,您没有足够的控制权来解决。在您检查HasExited 属性和检查MainWindowTitle 属性的时间之间,操作系统始终能够终止您正在处理的进程(任意或由于进程中的某些故障)。

Process 类在强制获取异常方面没有多大作用,但它已经足够了。特别是,调用Refresh() 会强制该类“忘记”它所知道的有关进程的任何信息,以便在您再次请求时重新检索信息。这包括进程的主窗口句柄。

Process 类使用本机窗口枚举函数来搜索已知进程 ID 的窗口句柄。由于进程已经退出,它找不到句柄,返回一个NULL 值(IntPtr.Zero 在托管术语中)。在看到 null 返回值时,Process 类会强制调用 InvalidOperationException


唯一可靠的解决方案是始终准备好捕获异常。在检查状态和尝试做一些依赖它的事情之间,状态总是有可能发生变化。


虽然是学术性的,但我发现有趣的是,如果您设置 EnableRaisingEvents 属性,Process 类可以(并且通常)更有效地检测退出的进程并引发异常。

特别是,当设置EnableRaisingEvents 属性时,Process 类注册以在进程句柄发出信号时由操作系统通知(通过线程池的RegisterWaitForSingleObject() 方法)。 IE。在这种情况下,Process 类甚至不需要搜索主窗口句柄,因为如果进程退出,它几乎会立即得到通知。

(当然,在一个非常小的机会窗口内,仍然可能存在内部竞争条件,因为当Process 类检查已退出状态时通知可能尚未到达,但该过程可能仍然在Process 类枚举窗口之前已退出)。

无论如何,这最后一点不会影响基本答案;这只是我在浏览the Process source code 时学到并发现有趣的一些琐事。 :)

【讨论】:

  • 他们确实在 enableraisingevents 方面做得很棒。
猜你喜欢
  • 2022-01-23
  • 2018-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多