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()
的位置?p.toHandle().parent().get().pid()
时没有这种问题?我该怎么做才能使问题不再发生?例如,即使我在子进程中运行了一个短暂的程序,如何使子进程的寿命更长,以便p.pid()
可以正常工作?
谢谢。
答案 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脚本,该脚本在执行所传递的命令后便会进入睡眠状态,但是我觉得这种解决方案充其量是笨拙的。