24

在 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 表示它们是僵尸进程。launchd第三列中的 236 是父 PID,在这种情况下属于我的用户。

请注意,某些过程已经存在数天了。在这段时间里,我已经退出并重新打开了几次 Xcode。

有谁知道为什么会发生这种情况,或者这是否应该引起警惕?

4

2 回答 2

15

在一些特别繁重的 Xcode 会话之后,我的 MBP 听起来像是被要求执行 STARNET 初始化程序,我决定花几分钟时间研究这个僵尸进程的废话......毕竟,一个不能 fork 的 Unix 机器是一个没用的 Unix 盒子。我可能有一些好消息。希望我们会看到。在此处在 10.8.2 上运行 Xcode 4.6。

无论 GDB 或 LLDB 的使用如何,僵尸问题似乎都会发生。在模拟器中运行的应用程序归调试进程所有——GDB 或 LLDB 的“调试服务器”。当您点击“停止”时,模拟器中运行的应用程序进程会变成僵尸。这听起来像是一个不干净的关机序列。

在预感而不是点击“停止”时,我暂停了应用程序,并在调试控制台(在我的情况下为 LLDB)中,我使用“进程分离”从正在运行的应用程序中分离出来。快速 ps 验证调试服务器不再运行......到目前为止一切顺利!现在,该应用程序仍在模拟器本身中运行,只是没有处于调试状态。事实上,现在点击停止按钮是无操作的。

在模拟器中点击主页按钮返回跳板,然后双击主页按钮并手动关闭应用程序。转到您的命令行并寻找僵尸......没有僵尸!耶。

所以......下一步是看看是否有一种合理的方法可以通过 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 运行时不允许进行正确的关闭和清理——事实上,即使当进程不再存在。碰巧我们的 iOS 和 OS X 运行时是同一个——因此启动时拥有僵尸。

所以我认为所有这一切的解决方案是在 iOS 级别确定一个干净的关闭程序,并且至少能够通过调试控制台执行该程序。将更多地研究 UIApplicationExitsOnSuspend 标志,看看我是否可以在运行时设置必要的位(而不是 plist),以便在调试分离时干净地关闭应用程序......

于 2013-03-24T17:36:48.073 回答
3

它们不会特别占用很多空间。

这似乎是 Xcode 机器不正确地杀死子进程的产物。

我有同样的问题,但我注意到,在我的情况下,僵尸属于 ppid 271,它是以我的名义调用 launchd,而不是整个系统。

我很好奇如果我杀死或停止该过程会发生什么。

无论如何,注销可能会清除僵尸。并且肯定会重新启动,但在我的书中,这是要避免的。


哦,那很糟糕不要杀死您的 launchd,它会毫不客气地杀死您的会话,但不会让您恢复它,例如给您一个登录屏幕。

由于停止 Xcode,我将不得不看看我是否将僵尸留在了后面。似乎这里可能有一些愚蠢的事情。如果您的进程不等待一个孩子,它就会变得僵化。如果父母死了,我认为接下来会得到它,在这种情况下是launchd。Launchd 应该等待()它,但也许那会让人感到困惑?

于 2012-11-15T11:33:09.150 回答