Runtime.exec() 错误:在未提供 Process 对象的情况下挂起
无论我使用 this:
process = Runtime.getRuntime().exec("logcat -d time");
还是 that:
process = new ProcessBuilder()
.command("logcat", "-d", "time")
.redirectErrorStream(true)
.start();
我都会得到相同的结果:它经常挂在 exec() 或 start() 调用中,无论我尝试做什么! 运行此线程的线程甚至不能用 Thread.interrupt() 中断!子进程肯定已启动,如果被终止,上述命令将返回。
这些调用可能会在第一次尝试时失败,因此无法读取它们的输出!我也可以使用简单的“su -c Kill xxx”命令行,同样的结果!
编辑:开始调试 NDK 项目中的 java_lang_ProcessManager.cpp 文件并提供一些调试日志!所以这是我到目前为止发现的,在 fork() 之后,父进程执行此操作:
int result;
int count = read(statusIn, &result, sizeof(int)); <- hangs there
close(statusIn);
虽然子进程不应该阻止它:这就是子进程所做的(如果完全启动!) :
// Make statusOut automatically close if execvp() succeeds.
fcntl(statusOut, F_SETFD, FD_CLOEXEC); <- make the parent will not block
// Close remaining unwanted open fds.
closeNonStandardFds(statusOut, androidSystemPropertiesFd); <- hangs here sometimes
...
execvp(commands[0], commands);
// If we got here, execvp() failed or the working dir was invalid.
execFailed:
int error = errno;
write(statusOut, &error, sizeof(int));
close(statusOut);
exit(error);
孩子可能会因为 2 个可重现的原因而失败: 1-子代码没有运行,但父代码认为它正在运行! 2- 子块开启 closeNonStandardFds(statusOut, androidSystemPropertiesFd);
无论哪种情况,父级中的 read(statusIn...) 都会以死锁结束!并且子进程处于死亡状态(并且无法访问,pid 未知,没有 Process 对象)!
Whether I use this:
process = Runtime.getRuntime().exec("logcat -d time");
or that:
process = new ProcessBuilder()
.command("logcat", "-d", "time")
.redirectErrorStream(true)
.start();
I get the same results: it often hangs within the exec() or start() call, no matter what I tried to do!
The thread running this cannot even be interrupted with Thread.interrupt()! The child process is definitely started and if killed the above commands return.
These calls may fail on first attempt, so THERE IS NO WAY TO READ THEIR OUTPUT! I can also use a simple "su -c kill xxx" command line, same result!
EDIT: Started debugging the java_lang_ProcessManager.cpp file in an NDK project with some debugging logs! So here is what I found so far, after the fork() the parent does this:
int result;
int count = read(statusIn, &result, sizeof(int)); <- hangs there
close(statusIn);
Though the child process is not supposed to block on it: That's what the child does (if started at all!):
// Make statusOut automatically close if execvp() succeeds.
fcntl(statusOut, F_SETFD, FD_CLOEXEC); <- make the parent will not block
// Close remaining unwanted open fds.
closeNonStandardFds(statusOut, androidSystemPropertiesFd); <- hangs here sometimes
...
execvp(commands[0], commands);
// If we got here, execvp() failed or the working dir was invalid.
execFailed:
int error = errno;
write(statusOut, &error, sizeof(int));
close(statusOut);
exit(error);
The child can fail for 2 reproducible reasons:
1- child code is not running, but the parent believes it is!
2- child blocks on
closeNonStandardFds(statusOut, androidSystemPropertiesFd);
In either case the read(statusIn...) in the parent ends in deadlock! and a child process is left dead (and cannot be accessed, pid unknown, no Process object)!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
此问题在 Jelly Bean (Android 4.1) 中已修复,但在ICS (4.0.4) 我想它永远不会已在 ICS 中修复。
This problem is fixed in Jelly Bean (Android 4.1) but not in ICS (4.0.4) and I guess it will never be fixed in ICS.
上述解决方案在任何方面都没有被证明是可靠的,导致在某些设备上出现更多问题!
所以我恢复到标准 .exec() 并继续挖掘...
查看挂起的子代码,我注意到子进程将在尝试关闭从父进程继承的所有文件描述符时挂起 (在 exec() 调用中创建的除外)!
因此,我在整个应用程序代码中搜索任何 BufferedReader/Writer 和类似的类,以确保在调用 exec() 时这些类将被关闭!
当我在调用 exec() 之前删除最后一个打开的文件描述符时,问题的频率大大降低,并且实际上再也没有发生过。
注意:确保 SU 二进制文件是最新的,它实际上也可能导致此问题!
享受您的搜索;)
Above solution didn't prove to be reliable in any ways, causing more issues on some devices!
So I reverted back to the standard .exec() and kept digging...
Looking at the child code that hangs, I noticed the child process will hang while trying to close all file descriptors inherited from the parent (except the one created within the exec() call) !
So I search the whole app code for any BufferedReader/Writer and similar classes to make sure those would be closed when calling exec()!
The frequency of the issue was considerably reduced, and actually never occured again when I removed the last opened file descriptor before calling exec().
NB: Make sure SU binary is up-to-date, it can actually cause this issue too!
Enjoy your search ;)
Bionic 中的错误修复几个月前已提交,但是它仍然没有被包含在Android 4.0.4中。
Bug fix in Bionic was commited monthes ago, but it still hasn't been included in Android 4.0.4.
我在 ICS 上也遇到同样的问题(似乎在 Android <4 上工作正常)。你找到解决办法了吗?
一个简单的解决方法可能是在带有超时连接的专用线程中调用“exec”方法,以便可以“检测到”这种情况(是的,我知道这不是很优雅......)
I have the same problem on ICS (seem to works fine on Android < 4). Did you find a solution?
A simple workaround could be to call the "exec" method in a dedicated thread with a timeout-join so that this situation could be "detected" (yes I know it's not very elegant...)