即使脚本的执行完成,Java也会挂起

前端之家收集整理的这篇文章主要介绍了即使脚本的执行完成,Java也会挂起前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在尝试从我的 java代码中执行一个脚本,如下所示:
Process p = Runtime.getRuntime().exec(cmdarray,envp,dir); // cmdarray is a String array
// consisting details of the script and its arguments

final Thread err = new Thread(...); // Start reading error stream
err.start();
final Thread out = new Thread(...); // Start reading output stream
out.start();
p.waitFor();
// Close resources

脚本的执行结束了(它的pid已不复存在),但java仍停留在进程的waitFor()方法上!
是的,我正在2个独立的线程中读取输出错误流.是的,他们最后加入(在waitFor()之后).

该脚本基本上安装了几个RPM(如10个左右)并配置它们.因此脚本运行了60多秒.

它看起来类似于以下内容

#!/bin/sh

#exec 3>&1 >/var/log/some_log 2>&1

# If the above line is uncommented,Java recognizes that the 
# process is over and terminates fine.

tar xzf a-package-having-rpms.tar.gz
cd unpacked-folder
(sh installer-script.sh) #This installs 10 odd rpms in a subshell and configures them
cd ..
rm -rf unpacked-folder

exit 0

令人震惊的是,如果我在脚本中放置以下行(在顶部),Java了解脚本已经结束并且它完美地终止了该过程.

exec 3>&1 > /var/log/some_log 2>&1

对于记录,脚本不会生成任何输出.零个字符!所以把exec语句放在那里毫无意义!

但是,神奇地说,在脚本中放置exec语句会使java工作!
为什么??

我怎样才能避免在脚本中出现那种不合逻辑的exec语句?

如果您对installer-script.sh的内容感兴趣,那么:

#!/bin/sh

exec 3>&1 >>/var/log/another-log.log 2>&1
INSDIR=$PWD
RPMSDIR=$INSDIR/RPMS
cd $RPMSDIR
#
rpm -i java-3.7.5-1.x86_64.rpm
rpm -i --force perl-3.7.5-1.x86_64.rpm
rpm -i --nodeps MysqL-libs-5.0.51a-1.vs.i386.rpm
rpm -i --nodeps MysqL-5.0.51a-1.vs.i386.rpm
rpm -i --nodeps MysqL-server-5.0.51a-1.vs.i386.rpm
rpm -i --nodeps perl-DBD-MysqL-3.0007-2.el5.i386.rpm
rpm -i --nodeps perl-XML-Parser-2.34-6.1.2.2.1.i386.rpm
.
.
.

现在,为什么Java需要exec命令才能知道进程结束了?
我怎么能避免那个exec?,尤其是因为第一个脚本不产生任何输出.

屏住呼吸等待答案!

解决方法

我的猜测是,Java不会认为脚本已经结束,直到它通过stdin / stdout / stderr传递给它的管道被子进程关闭.也就是说,stdin上没有更多活动的读者进程,stdout / stderr上没有更多活动的编写器进程.

当您在管道上读取时,在没有更多进程打开管道输出之前,您不会收到文件结束指示.因此,如果进程分叉和新进程继承了一个打开的文件句柄,那么原始进程终止,仍然有一个打开文件的进程,读者仍然会等待.

与您正在编写的管道类似,在最后一个读取器关闭管道之前,您不会收到“断管”信号.

当您的脚本分离继承stdin / stdout / stderr的后台任务(如新安装的服务)时,通常会出现此问题.

通过使用exec,您明确地打破了这些管道的继承链,以便后台进程不使用它们.

如果在Linux上,检查/ proc / * / fd是否有任何新服务,并查看它们的stdin / stdout / stderr是否与java进程传递给脚本的管道相同.

运行/etc/init.d/xxx脚本时经常会出现同样的情况:当您从命令行运行它们时它们会正常完成,但是当您从某种监视器运行它们时它们似乎会挂起.

编辑:

您说安装程序脚本包含以下行:

exec 3>&1 >>/var/log/another-log.log 2>&1

第一项,3>& 1,将stdout克隆到文件描述符3(参见man bash中的Redirections).据我所知,fd 3没有特别的意义.然后它通过打开/var/log/another-log.log替换stdout并通过克隆stdout替换stderr.请参阅bash手册页的“重定向”部分

这意味着新文件描述符3在最初作为STDOUT传入的管道上打开以进行写入.期望成为系统服务守护程序的程序通常会关闭文件描述符0(STDIN),1(STDOUT)和2(STDERR),但可能不会打扰任何其他文件描述符.此外,既然shell已打开FD-3,它会将该打开文件传递给它执行的任何命令,包括后台命令.

你知道安装程序打开FD 3有什么特别的原因吗?我的猜测是,如果您只是从安装程序中删除“3>& 1”术语,您的问题就会得到解决.这将允许您完全从脚本中删除exec

猜你在找的Java相关文章