给出一个从python开始的子进程,其代码类似于:
import subprocess
p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.communicate()
print('Return code: {}'.format(p.returncode))
根据official documentation,可以检查子进程是否被信号终止:
负值-N表示该子对象已被信号N终止(仅适用于POSIX)。
但仅在POSIX平台上。
在Windows平台上,有没有办法检查进程是否被信号终止(不在乎哪个)?
我在运行googletest测试时遇到了这个问题。 break-on-failure CLI flag test在Windows平台(VC14,VS2017)上失败,但在POSIX平台(2x Ubuntu,2x macOS)上运行良好。
在命令行上手动获得以下结果:
> .\googletest-break-on-failure-unittest_.exe --gtest_break_on_failure
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from Foo
[ RUN ] Foo.Bar
<some path>\googletest\test\googletest-break-on-failure-unittest_.cc(52): error: Expected equality of these values:
2
3
> echo %ERRORLEVEL%
-2147483645
但是,调用此测试的python包装器会收到2147483651
(正数)。
(我刚刚在this line之前添加了打印品)
请注意,它们以十六进制表示数字0xFFFFFFFF80000003
(负数)和0x80000003
(正数),并且未对返回码进行进一步处理。 (请参见here)
为什么要这样修改返回码?
PS:是的,我已经检查了GTEST_OS_WINDOWS
和GTEST_HAS_SEH
在C ++代码中是否正确。
答案 0 :(得分:1)
CMD的ERRORLEVEL
环境变量是一个32位带符号的值。正范围是0x00000000-0x7FFFFFFF(0直到2147483647),负范围是0x80000000-0xFFFFFFFF(-2147483648直到-1)。当进程由于未处理的异常而退出时,状态通常是异常代码,通常是32位带符号的NTSTATUS
值。就是说,没有什么能阻止我们用超过0x7FFFFFFF的无符号退出代码调用ExitProcess
或TerminateProcess
,因此CMD中的ERRORLEVEL
负值本身并不意味着进程异常退出。 / p>
在这种情况下,-2147483645是NTSTATUS
代码STATUS_BREAKPOINT
(0x80000003)。这源自Google Test的gtest_break_on_failure
选项,该选项调用WinAPI DebugBreak
。对于x86体系结构,后一个函数仅执行一条int 3
指令(软件中断3),该指令被困在内核中以引发断点异常。
通常在没有附加调试器的情况下,这将执行默认的Windows未处理异常处理程序,该处理程序将调用Windows Error Reporting(请参见下文)。但是问题中链接的代码将ExitWithExceptionCode
设置为应用程序的未处理异常过滤器。该过滤器仅通过exit(exception_pointers->ExceptionRecord->ExceptionCode)
退出。
对于Unix信号,Windows内核未实现它们。 C运行时实现了标准C所需的六个。在控制台应用程序中,标准SIGINT
信号与控制台的CTRL_C_EVENT
相关联。非标准SIGBREAK
信号用于其他控制台控制事件,包括CTRL_BREAK_EVENT
和CTRL_CLOSE_EVENT
。这些事件的默认处理程序调用{{1}}。因此,要从字面上回答问题,以确定进程是否被控制台“信号”杀死,请检查ExitProcess(STATUS_CONTROL_C_EXIT)
(0xC000013A或-1073741510)。
处理未处理的异常
如果将调试器附加到进程调试端口,则内核会向其发送优先机会异常事件。如果它不处理异常,则内核将异常分配到线程的向量异常处理程序和结构化异常处理程序链(基于堆栈,与当前帧相反检查)中。我们将假定该异常未被任何人处理。
最后检查的帧是线程启动功能STATUS_CONTROL_C_EXIT
的帧。该帧的处理程序RtlUserThreadStart
传递了一个调度记录,使它可以确定作用域的异常过滤器。给定先前通过_C_specific_handler
设置的过滤器,该过滤器又调用该进程的未处理异常过滤器。在进程启动时,kernelbase.dll的初始化例程将其设置为恰当命名的函数RtlSetUnhandledExceptionFilter
。
如果连接了调试器,则UnhandledExceptionFilter
返回UnhandledExceptionFilter
。随后,内核向调试器发送第二次异常事件。如果调试器没有处理异常,则内核尝试将事件发送到子系统,即Windows会话服务器csrss.exe。服务器继而将故障中继到Windows错误报告(WER)服务,该服务最终将终止该过程。
如果未附加调试器,则EXCEPTION_CONTINUE_SEARCH
会调用应用程序的未处理异常过滤器(如果以前是通过SetUnhandledExceptionFilter
设置的)。如果应用程序过滤器返回UnhandledExceptionFilter
或EXCEPTION_CONTINUE_EXECUTION
,则EXCEPTION_EXECUTE_HANDLER
将此值返回到帧处理程序。
如果应用程序未设置自己的过滤器,或者其过滤器返回UnhandledExceptionFilter
,则EXCEPTION_CONTINUE_SEARCH
接下来将检查作业{{1}的工作,进程和线程错误模式}}。该标志禁用错误报告,在这种情况下,过滤器仅返回UnhandledExceptionFilter
。
如果允许错误报告,则SEM_NOGPFAULTERRORBOX
调用Windows错误报告(WER)服务。 WER处于循环状态,其行为最终取决于注册表中的配置方式,组策略以及当前Windows版本的默认行为。 WER可以基于系统“ AeDebug”设置创建并附加调试器过程。如果附加了调试器,则EXCEPTION_EXECUTE_HANDLER
返回UnhandledExceptionFilter
,就像已经附加了调试器一样。否则返回UnhandledExceptionFilter
。
最终,如果它为“未处理”异常执行异常处理程序,则首先通过EXCEPTION_CONTINUE_SEARCH
将堆栈退回到EXCEPTION_EXECUTE_HANDLER
帧,这将调用任何RtlUserThreadStart
终止中间帧中的处理程序。异常处理程序的执行上下文通过RtlUnwindEx
恢复,并且异常代码在整数返回寄存器(例如x64中的finally
)中设置。最后,处理程序通过RtlRestoreContext
自行终止。