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 的僵尸进程的讨论。似乎唯一的潜在问题可能是一段时间后pids 用完。 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 无法运行 iOS 模拟器

应用程序卡在带有 Xcode 11 测试版的 iOS 13 测试版模拟器上,但在带有 Xcode 10 的 iOS 13 测试版模拟器上运行良好

Xcode 的 OCUnit 测试能否在 iOS 设备而不是模拟器上运行

在较旧的 iOS 模拟器上运行时 Xcode 10 测试失败 - “无法加载测试包......找不到合适的图像”

可以在 IOS 8 (Xcode 6) 模拟器中测试应用内购买吗?