Xcode 在运行 iOS 测试/模拟器后离开僵尸进程
Posted
技术标签:
【中文标题】Xcode 在运行 iOS 测试/模拟器后离开僵尸进程【英文标题】:Xcode leaving zombie processes after running iOS tests/simulator 【发布时间】:2012-10-23 23:14:57 【问题描述】:在 ios 应用上使用 Xcode 工作了几天后,我注意到有超过 100 个僵尸进程在闲逛。似乎每次我运行单元测试都会有一个,每次我在模拟器中运行完整的应用程序时可能会有一个。这是一个示例(已清理和截断):
> ps -efj | grep $PRODUCT_NAME
502 2794 236 0 Wed12AM ?? 0:00.00 (MyProduct) me 2794 0 1 Z ??
502 2843 236 0 Wed01AM ?? 0:00.00 (MyProduct) me 2843 0 1 Z ??
502 2886 236 0 Wed01AM ?? 0:00.00 (MyProduct) me 2886 0 1 Z ??
...
502 13711 236 0 Thu11PM ?? 0:00.00 (MyProduct) me 13711 0 1 Z ??
502 13770 236 0 Thu11PM ?? 0:00.00 (MyProduct) me 13770 0 1 Z ??
502 14219 236 0 10:35AM ?? 0:00.00 (MyProduct) me 14219 0 1 Z ??
502 14280 236 0 10:38AM ?? 0:00.00 (MyProduct) me 14280 0 1 Z ??
倒数第二列的 Z 表示它们是僵尸进程。第三列的 236 是父 PID,在这种情况下属于我的用户的launchd
。
请注意,有些流程是几天前的。在此期间,我已经退出并重新打开了几次 Xcode。
有谁知道为什么会发生这种情况,或者这是否应该引起警惕?
【问题讨论】:
我有 437 个僵尸并且还在增加。迟早会发生的唯一问题是您无法启动任何新进程,因此请记住僵尸。我花了一段时间才弄清楚为什么我不能再编译了。另见***.com/a/7860828/457406 @esker 除了重启之外,你有没有想过解决这个问题? 不,据我所知,它就是这样工作的。请参阅下面关于最终属于launchd
的僵尸进程的讨论。似乎唯一的潜在问题可能是一段时间后pid
s 用完。
Xcode 4.6.3 修复了这个问题。
Zombie Processes from iPhone Simulator?的可能重复
【参考方案1】:
在一些特别繁重的 Xcode 会话之后,我的 MBP 听起来像是被要求执行 STARNET 初始化程序,我决定花几分钟时间研究这个僵尸进程的废话......毕竟,一个 Unix 机器可以' t fork 是一个无用的 Unix 机器。我可能有一些好消息。希望我们会看到。在此处在 10.8.2 上运行 Xcode 4.6。
无论使用 GDB 还是 LLDB,僵尸问题似乎都会发生。在模拟器中运行的应用程序归调试进程所有——GDB 或 LLDB 的“调试服务器”。当您点击“停止”时,模拟器中运行的应用程序进程会变成僵尸。这听起来像是一个不干净的关机序列。
出于预感而不是点击“停止”,我暂停了应用程序,并在调试控制台(在我的情况下为 LLDB)中,我使用“进程分离”从正在运行的应用程序中分离出来。快速 ps 验证调试服务器不再运行……到目前为止一切顺利!现在,该应用程序仍在模拟器本身中运行,只是没有处于调试状态。事实上,现在点击停止按钮是无操作的。
在模拟器中点击home键回到跳板,然后双击home键手动关闭应用。转到您的命令行并寻找僵尸......没有僵尸!耶。
所以...下一步是看看是否有合理的方法可以通过 python 脚本等执行此或类似的关闭过程。'如果你在 GDB 上,这对你没有帮助。如果我可以通过单个调试控制台命令彻底关闭,那么只需习惯不点击损坏的停止按钮即可。也许有一个资源黑客可以完全禁用它...... :)
编辑#1:几个有趣的花絮......
1.) 在 xcode 中点击停止按钮会直接终止调试和应用程序进程——没有任何尝试彻底关闭。应用程序委托 applicationWillTerminate 和 applicationDidEnterBackground 中的 Printf 调试显示正在运行的应用程序因偏见而被终止 - 控制台中未显示 NSLog。
2.) 在调试控制台中调用 [UIApplication terminateWithSuccess] 将导致应用程序“正确”终止,但仍会留下僵尸......有趣的是,如果您设置了断点,应用程序将不会终止:
(lldb) expression
Enter expressions, then terminate with an empty line to evaluate:
[(UIApplication *)[UIApplication sharedApplication] terminateWithSuccess]
error: Execution was interrupted, reason: breakpoint 2.1.
The process has been returned to the state before execution.
(lldb) breakpoint disable 2.1
1 breakpoints disabled.
(lldb) expression
Enter expressions, then terminate with an empty line to evaluate:
[(UIApplication *)[UIApplication sharedApplication] terminateWithSuccess]
2013-03-25 01:28:00.186 iPhone Testbed[9481:c07] -[AppDelegate applicationWillTerminate:]
(lldb)
所以应用程序经历了某种关闭过程,并且将终止在控制台中显示,但我们仍然有一个僵尸,所以它仍然不是一个干净的关闭。
我认为这整件事与应用程序在 iOS 运行时中进入后台有关。当直接修改进程时(通过停止按钮、kill 命令、调试控制台的东西等),iOS 运行时不允许进行正确的关闭和清理——事实上,springboard 仍然认为应用程序在后台运行,即使进程不再存在。碰巧我们的 iOS 和 OS X 运行时是同一个——因此我们启动了僵尸。
所以我认为所有这一切的解决方案是在 iOS 级别确定一个干净的关闭程序,并且至少能够通过调试控制台执行该程序。将更多地研究 UIApplicationExitsOnSuspend 标志,看看我是否可以在运行时设置必要的位(而不是 plist),以便在调试分离时干净地关闭应用程序......
【讨论】:
非常有趣的答案。到目前为止,我在这个主题上见过的最完整的。期待任何发展! 感谢您提供详细信息。让launchd 自动收割归于它的僵尸有意义吗? (或者至少有一个 launchctl 选项可以这样做?)【参考方案2】:它们不会占用太多空间。
这似乎是 Xcode 机器不正确地杀死子进程的产物。
我有同样的问题,但我注意到僵尸属于,就我而言,ppid 271,它是以我的名义调用 launchd,而不是整个系统。
我很好奇如果我杀死或中断该进程会发生什么。
无论如何,退出系统可能清除僵尸。并且肯定会重新启动,但在我的书中,这是要避免的。
哦,那很糟糕。 不要杀死你的 launchd,它会毫不客气地杀死你的会话,但不会让你恢复它,比如给你一个登录屏幕。
我必须看看我是否因为停止 Xcode 而把僵尸留在了后面。似乎这里可能有一些愚蠢的事情。如果您的进程不等待一个孩子,它就会变得僵化。如果父母死了,我认为接下来会得到它,在这种情况下是launchd。 Launchd 应该等待()它,但也许这会让人感到困惑?
【讨论】:
They don't particularly take up a lot of room.
是的,除了它需要更重要的东西:吃掉你可以创建的进程数量。 fork
将失败,因此您无法事件 exec kill
@2mia 完全正确,几天没有重新启动我的开发系统后,我一直收到这个 fork 错误。
@2mia 是对的,几天没有重新启动后,我经常收到 fork 错误。必须重新启动非常烦人。任何其他不涉及增加允许进程数量的修复/解决方法?
实际上你可以在杀死launchd后重新登录。一旦你杀死了launchd,你必须点击快捷键shift-option-command-Q来“注销”。
我不记得那个键序列了。谢谢,我下次试试,如果我记得。 :)以上是关于Xcode 在运行 iOS 测试/模拟器后离开僵尸进程的主要内容,如果未能解决你的问题,请参考以下文章
仪器无法在 Xcode 4.4 的真实 iOS 设备中运行 GUI 测试脚本
应用程序卡在带有 Xcode 11 测试版的 iOS 13 测试版模拟器上,但在带有 Xcode 10 的 iOS 13 测试版模拟器上运行良好
Xcode 的 OCUnit 测试能否在 iOS 设备而不是模拟器上运行