Creating a Nohup Process in Java

2019-09-02 19:47发布

问题:

Using ProcessBuilder, I've been trying to create an independent process that doesn't get terminated when the JVM gets terminated, but nothing seems to work.

I've tried /usr/bin/nohup commands, but that still seems to terminate when the JVM that launched it is terminated. Is there any way to accomplish this in Java?

回答1:

Well, first things first lets write a test script that validates what you're seeing:

$ cat /tmp/test.sh 
#!/bin/bash

for sig in SIGINT SIGTERM SIGHUP; do
  trap "echo Caught $sig" $sig
done

echo Traps registered, sleeping
sleep 10
echo Done sleeping, exiting

When I invoke this via:

new ProcessBuilder("/tmp/test.sh").inheritIO().start().waitFor(1, TimeUnit.SECONDS);

I see the Java process terminate after 1 second (since waitFor() timed out), but the subprocess keeps going (the Done sleeping message is printed to the console after the JVM exits). This lines up with what's discussed in this question.

So the first thing I'd suggest doing is validating your initial assumption that the subprocess is in fact being killed; perhaps something else is going wrong that causes it to die for another reason. Using .inheritIO() can help debugging, in case the subprocess is generating error messages you're not seeing.


All that said, nohup may not be what you want, as 'that other guy' notes; nohup only causes a process to ignore SIGHUP, and it will still respect other signals, notably SIGINT and SIGTERM (Process.destroy() sends a SIGTERM, for instance). See this question for more.

As with all problems in programming, introducing more layers would probably help :)

Create a shell script that handles the desired disowning logic, e.g.

$ cat start-and-disconnect.sh
#!/bin/bash

# obviously tweak this as necessary to get the behavior you want,
# such as redirecting output or using disown instead of nohup
nohup "$@" & 

The advantage here is that Bash (or whatever shell you prefer) has better process-management control than Java, and now you can test it in isolation, without needing to compile and run your Java application.

Once you're happy with the script, your Java code simply becomes:

new ProcessBuilder("/path/to/start-and-disconnect.sh", "/path/to/your_binary.sh")
    .inheritIO().start().waitFor();

You can safely .waitFor() the call to complete, since start-and-disconnect.sh will exit after starting its subprocess.