问题背景
如果要在Java中调用shell脚本时,可以使用Runtime.exec或ProcessBuilder.start。它们都会返回一个Process对象,通过这个Process可以对获取脚本执行的输出,然后在Java中进行相应处理。例如,下面的代码:
try
{
Process process = Runtime.getRuntime().exec(cmd);
process.waitFor();
//do something ...
}
catch (Exception e)
{
e.printStackTrace();
}
通常,安全编码规范中都会指出:使用Process.waitfor的时候,可能导致进程阻塞,甚至死锁。 那么这句应该怎么理解呢?用个实际的例子说明下。
问题描述
使用Java代码调用shell脚本,执行后会发现Java进程和Shell进程都会挂起,无法结束。
Java代码 processtest.java
try
{
Process process = Runtime.getRuntime().exec(cmd);
System.out.println("start run cmd=" + cmd);
process.waitFor();
System.out.println("finish run cmd=" + cmd);
}
catch (Exception e)
{
e.printStackTrace();
}
挂起原因
主进程中调用Runtime.exec会创建一个子进程,用于执行shell脚本。子进程创建后会和主进程分别独立运行。
因为主进程需要等待脚本执行完成,然后对脚本返回值或输出进行处理,所以这里主进程调用Process.waitfor等待子进程完成。
通过shell脚本可以看出:子进程执行过程就是不断的打印信息。主进程中可以通过Process.getInputStream和Process.getErrorStream获取并处理。
这时候子进程不断向主进程发生数据,而主进程调用Process.waitfor后已挂起。当前子进程和主进程之间的缓冲区塞满后,子进程不能继续写数据,然后也会挂起。
这样子进程等待主进程读取数据,主进程等待子进程结束,两个进程相互等待,最终导致死锁。