我有一个C ++ Windows程序无法设置退出代码。该程序非常复杂,我目前无法通过简单的测试用例重现这一点。我知道该程序调用{{1}},因为我在该行上有一个断点。在我跳过它之后,调试器(VS2010)立即打印exit(1)
当我从shell运行它时,The program program.exe has exited with code 0 (0x0).
也被设置为0.
我使用%ERRORLEVEL%
和普通的subsystem:console
(没有WinMain)。
这仅发生在Windows Server 2008 R2上,而不是在我的Windows 8.1笔记本电脑上。我在两者上运行相同的可执行文件。
我尝试使用main
,exit
,_exit
和ExitProcess
(违规通知在return
),但似乎没有一个有任何影响。我也试图返回其他代码,也没有结果。
有一个similar question,但我无法重现其中描述的结果。我的程序确实使用了线程。
如何调试此问题?我很困惑。
答案 0 :(得分:11)
我曾尝试使用exit,_exit,ExitProcess和return
您已经取消了所有合理的解释,特别是使用ExitProcess()。只剩下一种可能性,你需要尝试TerminateProcess()。如果仍然没有设置退出代码,那么你需要将该机器推出第4层窗口。
但期望它现在有效。 ExitProcess()和TerminateProcess()之间的区别在于前者确保终止通知所有DLL。用fdwReason = DLL_PROCESS_DETACH调用它们的DllMain()函数。这让DLL有机会做一些像调用Exit / TerminateProcess()本身一样的东西,从而搞砸了退出代码。
如果您没有所有源代码,那么找到这样的DLL可能会很困难。也可能是一个注入的,这些天周围有太多。最好的办法是在系统调用上设置一个断点,这样你就可以在行为中捕获它,你可能想要这样做。
进入main()后,使用Debug>新断点>在功能处休息并输入{,,ntdll.dll}_NtTerminateProcess@8
。按F5,调试器现在在程序终止之前停止。查看调用堆栈以找到邪恶的行为者。
答案 1 :(得分:2)
在多线程程序中涉及exit(),_ exit(),ExitProcess()和其他程序的奇怪症状 - 特别是如果主机之间的症状不同 - 有一个变量的气味被不同的线程修改或访问,没有同步。
查看您链接到的另一个线程,看起来您正在使用volatile变量在线程之间进行通信,但不使用任何形式的同步(例如,访问该变量值的代码和修改该值的代码需要通过关键部分,互斥或类似构造进行合作。
这一点间接证据使气味变得更强烈。
我怀疑的基本问题是将变量声明为volatile既不必要也不足以确保变量始终具有对程序有意义的值。特别是,当修改仅部分完成时,防止修改变量的线程被抢占,以及另一个线程尝试访问或修改受影响的变量是不够的。
如果你查阅Herb Sutter的一些文章(特别是那些关于他的"本周大师"系列中的线程同步的文章),你会发现为什么会这样的详细解释。其他作者也描述了这些事情,但萨特的文章是我记得的那些文章。
解决方案是引入一些同步方法,并为程序中的每个线程在访问或修改它们之间共享的变量之前虔诚地使用它。这可以避免各种问题(竞争条件,操作被中途抢占),这些问题会导致你所描述的症状。
通过使用调试器很少发现这些问题。原因是症状是一种紧急性质。在不同的执行线程中,必须同时发生几种不太可能且通常是独立的事件。调试器通常会改变程序中事件的时间,而时间是出现症状的关键考虑因素。
选项包括使关键变量成为原子(因此特定操作不能被抢占),关键部分(线程在程序中显式合作)或互斥(根据定义,允许不同程序中的线程在访问之前明确地合作共享内存)。
是的,这会在您的程序中引入一个瓶颈 - 每个线程必须会合并并可能彼此等待。这可能会影响程序的吞吐量。有些人主张使用volatile变量来避免这种担忧。通常情况下,结果是在长期运行的程序中出现间歇性症状,就像您在此问题和类似问题中所描述的那样#34;你链接到。
使用标准的同步方法(例如,在C ++ 11中引入)或Windows特定方式(WIN API函数)并不重要。重要的是你使用有意的同步方法,而不是仅仅使变量变为volatile。不同的同步选项有不同的权衡,因此您需要做出与您的计划需求相关的决定。
另一个考虑因素是发出所有线程的信号,以便它们干净地关闭,等到它们全部关闭,捕获它们的退出代码,然后退出程序。在运行main()的线程中执行此操作通常不太容易出错 - 这最终会启动进程,因此更有可能访问正确清理所需的信息。如果另一个线程决定程序需要退出,那么如果它需要回到main()来执行它,那就更好了。