您的当前位置:首页>新品 > 正文

世界时讯:Process类详解 相关类和方法介绍

来源:CSDN 时间:2022-12-20 09:11:41

Process类详解

一、相关类和方法介绍二、安全风险1. external processes block on I|O streams

一、相关类和方法介绍


(相关资料图)

ProcessBuilder是一个final类,Process是一个抽象类。ProcessBuilder.start()和 Runtime.exec()方法都被用来创建一个操作系统进程(执行命令行操作),并返回 Process子类的一个实例,该实例可用来控制进程状态并获得相关信息。

每个进程生成器ProcessBuilder对象管理这些进程属性:

命令 是一个字符串列表,它表示要调用的可执行外部程序文件及其参数(如果有)。环境 是从变量 到值 的依赖于系统的映射。工作目录 默认值是当前进程的当前工作目录,通常根据系统属性 user.dir 来命名。redirectErrorStream 属性 子进程的标准输出和错误输出是否被发送给发送给两个独立的流(Process.getInputStream() 和 Process.getErrorStream()),默认false发送。

Runtime.exec()可接受一个单独的字符串,这个字符串是通过空格来分隔可执行命令程序和参数的;也可以接受字符串数组参数/list。ProcessBuilder.start()只支持字符串数组参数。

创建的子进程没有自己的终端或控制台。它的所有标准 io(即 stdin,stdout,stderr)操作都将通过三个流(getOutputStream(),getInputStream(),getErrorStream()) 重定向到父进程。

// Runtime.exec最终是通过调用ProcessBuilder来真正执行操作的public Process exec(String[] cmdarray, String[] envp, File dir)    throws IOException {// 在 directory() 指定的工作目录中,利用 environment() 指定的进程环境,新进程将调用由 command() 给出的命令和参数。    return new ProcessBuilder(cmdarray)        .environment(envp)        .directory(dir)        .start();}

注意 ProcessBuilder 第一个参数必须是可执行程序,可以添加参数使用{"cmd", "/c"}或 {"/bin/bash", "-c"}。

二、安全风险

描述 java.lang.Process 对象描述进程可能需要通过其输入流对其提供输入,并且其输出流、错误流或两者同时会产生输出。不正确地处理这些外部程序可能会导致一些意外的异常、DoS,及其他安全问题。 一个进程如果试图从一个空的输入流中读取输入,则会一直阻塞,直到为其提供输入。因此,在调用这样的进程时,必须为其提供输入。 一个外部进程的输出可能会耗尽该进程输出流与错误流的缓冲区。当发生这种情况时,Java 程序可能会阻塞外部进程,同时阻碍Java程序与外部程序的继续运行。因此,在运行一个外部进程时,如果此进程往其输出流发送任何数据,则必须将其输出流清空。类似的,如果进程会往其错误流发送数据,其错误流也必须被清空。

处理建议 对于那些从来不会读取其输入流的进程,不对其提供输入非但无害,且还有益。而对于那些从来不会发送数据到其输出流或者错误流的进程,不对其输出流或者错误流进行清空同样是有益无害的。因此,只要能够保证进程不会使用这些流,那么在程序中可以忽略其输入流、输出流、以及错误流。

1. external processes block on I|O streams

原因 有些本机平台仅针对标准输入和输出流提供有限的=缓冲区大小,如果读写子进程的输出流或输入流迅速出现失败(如不断发送数据),而主进程调用Process.waitfor后已挂起,则可能导致子进程阻塞,进程间相互等待甚至产生死锁。

现有如下三种解决方法,缓冲区内容消费掉即可。

// Do not let external processes block on I|O streams// 场景一: 使用java.lang.ProcessBuilder.redirectErrorStream(boolean redirectErrorStream)方法即可清空流ProcessBuilder builder = new ProcessBuilder(cmds);builder.redirectErrorStream(true);try {process = builder.start();} catch (IOException e) {e.pringtStackTrace();}// 场景二:当出现IOException异常时不应该将IOException异常throws,使用try/catch对IOException单独捕获Process process = null;try {process = builder.start();} catch (IOException e) {e.pringtStackTrace();}String handleMessage = "";BufferedReader bufferedReader = new BufferedSReader(new InputStreamReader(process.getInputStream, StandardCharesets.UTF_8));try {while ((handleMessage = bufferedReader.readLine()) != null) {System.out.println(handleMessage);}} catch (IOException e) {e.pringtStackTrace();}try {bufferedReader.close();} catch (IOException e) {e.pringtStackTrace();}// 场景三:有时候我们可能需要调用系统外部的某个程序,此时就可以用Runtime.getRuntime().exec()来调用,他会生成一个新的进程去运行调用的程序,waitFor()方法也有很明显的弊端,因为java程序给进程的输出流分配的缓冲区是很小的,有时候当进程输出信息很大的时候回导致缓冲区被填满,如果不及时处理程序会阻塞,解决的方法就是处理缓冲区中的信息,开两个线程分别去处理标准输出流和错误输出流Process process = Runtime.getRuntime().exec(str);// 记录进程缓存错误信息final StringBuffer errorLog = new StringBuffer();final InputStream errorStream = process.getErrorStream();final InputStream inputStream = process.getInputStream();// 处理InputStream的线程new Thread() {@Override   public void run() {BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));       String line = null;       try {// 消费掉缓存中的数据           while ((line = in.readLine()) != null && !errorLog.toString().contains("ERROR")) {if (line != null) {errorLog.append(line);               }           }       } catch (IOException e) {// public RuntimeException(String message, Throwable cause)           throw new RuntimeException("[shell exec error]:" + errorLog, e);       } finally {try {inputStream.close();;           } catch (IOException e) {e.printStackTrace();           }       }   }}.start();// 处理errorStream的线程new Thread() {@Override   public void run() {BufferedReader err = new BufferedReader(new InputStreamReader(errorStream));       String line = null;       try {// 消费掉缓存中的数据           while ((line = err.readLine()) != null && !errorLog.toString().contains("ERROR")) {if (line != null) {errorLog.append(line);               }           }       } catch (IOException e) {throw new RuntimeException("[shell exec error]:" + errorLog, e);       } finally {try {errorStream.close();;           } catch (IOException e) {e.printStackTrace();           }       }   }}.start();logger.info("等待shell脚本执行完成");Thread.sleep(1000);// 异常终止if (errorLog != null && errorLog.length() > 0 && errorLog.toString().contains("ERROR")) {dispatchLogger.error("[shell exec error]:" + errorLog);   throw new RuntimeException("[shell exec error]:" + errorLog);}// 等待shell脚本执行完成process.waitFor();

标签:

最新新闻:

新闻放送
Top