调用ProcessHandle的parent()。get()时“不存在值”

时间:2019-04-14 11:42:47

标签: java processbuilder processhandle

import java.lang.ProcessBuilder.Redirect;

public class Main {    
  public static void main(String args[]){    
ProcessBuilder pb = new ProcessBuilder(args); 

try{
Process p = pb.start();
   System.out.println("child process is alive: " + p.isAlive());   
   System.out.println("child process pid: " + p.pid());
   System.out.println("parent of child process: " + p.toHandle().parent().get().pid());
   System.out.println("child process is alive: " + p.isAlive());      
   p.waitFor();

} catch (Exception e) {
    System.out.println("some exception with start process.");
    e.printStackTrace();    
}

  }
}

在Ubuntu 18.04上,我运行该程序以运行外部程序sleep 10,而没有任何问题:

$ java Main  sleep 10
child process is alive: true
child process pid: 20559
parent of child process: 20539
child process is alive: true
$

但是当我运行程序以运行外部程序echo hello

$ java Main  echo hello
child process is alive: false
child process pid: 18534
some exception with start process.
java.util.NoSuchElementException: No value present
    at java.base/java.util.Optional.get(Optional.java:148)
    at Main.main(Main.java:11)

为什么它在第get()行的p.toHandle().parent().get().pid()报告错误?

如果我注释掉第11行,它将正常运行。为什么会这样?

如果是因为孩子在第11行调用p.toHandle().parent().get().pid()之前退出了

  • 为什么NoSuchElementException发生在get()而不是早于第11行的toHandle()的{​​{1}}或parent()的位置?
  • 为什么在第10行调用p.toHandle().parent().get().pid()时没有这种问题?

我该怎么做才能使问题不再发生?例如,即使我在子进程中运行了一个短暂的程序,如何使子进程的寿命更长,以便p.pid()可以正常工作?

谢谢。

1 个答案:

答案 0 :(得分:2)

为什么使用parent()调用时从echo hello返回的可选内容为空?

因为子进程非常短,并且当您找到要求父进程的命令时,子进程不再存在。因此,无法再向操作系统查询该过程。

为什么调用getHandle()pid()时不会发生这种情况?

请注意,这些都不是Optional。这意味着API保证将在其中返回值。实际上,该实现是这样的:一旦操作系统创建了进程并在pb.start()中将其返回,便创建了该句柄-包括进程ID。因此,您拥有一个句柄,并且拥有一个进程ID-但是使用该ID的进程不能保证在使用时仍处于活动状态。

documentation明确告诉您不要对基础过程的活跃性或身份进行假设。

parent()调用当前是通过使用子代的pid查询操作系统来实现的。因此,在创建流程时,父级不属于流程记录的一部分,并且在您调用该流程时,它可能不再可用。

如何避免这种情况

每当您使用Optional时,请勿使用get。特别是在没有事先检查是否存在的情况下(并且使用isPresent ... get被认为是“反模式”)。当您看到API给您Optional时,请想想如果该Optional为空时该怎么做。不要做出文档中没有说服力的假设。

在这种情况下,如果找不到父项,您可能需要显示默认消息。例如:

System.out.println("parent of child process: "
    + p.toHandle().parent().map(ProcessHandle::pid)
                           .map(Object::toString)
                           .orElse("not available"));

或者您可以使用当前进程的pid作为默认值,或者可以使用的其他任何方法。如果您的工作绝对需要父母的身份,或者您可能抛出另一种例外。有关各种使用方法,请参见Optional的文档。

没有办法扩展基础过程-您可以使用一个shell脚本,该脚本在执行所传递的命令后便会进入睡眠状态,但是我觉得这种解决方案充其量是笨拙的。