This is my code in Java:
Runtime rt = Runtime.getRuntime();
Process p = rt.exec("nohup java -jar /root/xxxx.jar &");
This command can be executed, I can see that the service is started, but I have not seen the nohup log, why is this?
CodePudding user response:
Short story
Your expectations are based on observations which are actually irrelevant to java, basically that ("redirecting output to nohup.out
") does not work because processes spawned via java.lang.Runtime#exec
are not bound to tty
, thus nohup
wrapper
has no effect.
Long story
Q: Why do UNIX
guys need to use nohup
wrapper
when they want to spawn long-running process/program?
When we are working from shell
it's first three file descriptors (stdin
, stdout
and stderr
) are bound to tty
, for example:
]# ls -la /proc/self/fd/[0-2]
lrwx------ 1 root root 64 Aug 20 09:29 /proc/self/fd/0 -> /dev/pts/2
lrwx------ 1 root root 64 Aug 20 09:29 /proc/self/fd/1 -> /dev/pts/2
lrwx------ 1 root root 64 Aug 20 09:29 /proc/self/fd/2 -> /dev/pts/2
and any process spawned from shell
inherits those three file descriptors, for example:
# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
...
# another shell
# 65681 - pid of ping
# 65654 - pid of shell (parent)
]# ps -ef | grep ping
root 65681 65654 0 09:31 pts/2 00:00:00 ping 8.8.8.8
root 65700 65684 0 09:32 pts/3 00:00:00 grep --color=auto ping
]# ls -la /proc/65681/fd/[0-2]
lrwx------ 1 root root 64 Aug 20 09:32 /proc/65681/fd/0 -> /dev/pts/2
lrwx------ 1 root root 64 Aug 20 09:32 /proc/65681/fd/1 -> /dev/pts/2
lrwx------ 1 root root 64 Aug 20 09:32 /proc/65681/fd/2 -> /dev/pts/2
Q: what will happen if we leave our shell
?
]# kill -9 65654
]# ls -la /proc/65681/fd/[0-2]
ls: cannot access /proc/65681/fd/[0-2]: No such file or directory
]# ps -ef | grep 65681
root 65738 65684 0 09:40 pts/3 00:00:00 grep --color=auto 65681
Operating system was need to close tty
(we left shell
), so OS
sends SIGHUP
signal to all processes which was using (bound to) that tty
and closes tty
The purpose of nohup
wrapper
is following:
it inspects whether first three file descriptors (stdin
, stdout
and stderr
) are bound to tty
and if so it replaces them by /dev/null
, nohup.out
and nohup.out
:
]# nohup ping 8.8.8.8
nohup: ignoring input and appending output to 'nohup.out'
...
# another shell
# 65767 - pid of ping
# 65752 - pid of shell (parent)
]# ps -ef | grep ping
root 65767 65752 0 09:44 pts/2 00:00:00 ping 8.8.8.8
root 65773 65684 0 09:45 pts/3 00:00:00 grep --color=auto ping
]# ls -la /proc/65767/fd/[0-2]
l-wx------ 1 root root 64 Aug 20 09:45 /proc/65767/fd/0 -> /dev/null
l-wx------ 1 root root 64 Aug 20 09:45 /proc/65767/fd/1 -> /nohup.out
l-wx------ 1 root root 64 Aug 20 09:45 /proc/65767/fd/2 -> /nohup.out
now, trying to "leave" our shell
:
]# kill -9 65752
]# ls -la /proc/65767/fd/[0-2]
l-wx------ 1 root root 64 Aug 20 09:45 /proc/65767/fd/0 -> /dev/null
l-wx------ 1 root root 64 Aug 20 09:45 /proc/65767/fd/1 -> /nohup.out
l-wx------ 1 root root 64 Aug 20 09:45 /proc/65767/fd/2 -> /nohup.out
]# ps -ef | grep ping
root 65767 1 0 09:44 ? 00:00:00 ping 8.8.8.8
root 65781 65684 0 09:46 pts/3 00:00:00 grep --color=auto ping
our long-running application is continuing running, everyone is happy - that is nohup
wrapper
magic.
In regard to your Q...
If your goal it to re-implement the same using Java
you need to do the same in Java
and do not rely on nohup
wrapper
, below are some examples:
Let's try using "naive" implementation like:
public class Test {
public static void main(String[] args) throws Exception {
Runtime rt = Runtime.getRuntime();
rt.exec("ping 8.8.8.8");
Thread.sleep(1_000_000_000);
}
}
]# $JAVA_HOME/bin/javac Test.java
]# $JAVA_HOME/bin/java -cp . Test
# another shell
# 65890 - pid of ping
# 65873 - pid of java (parent)
]# ps -ef | grep ping
root 65890 65873 0 09:58 pts/2 00:00:00 ping 8.8.8.8
root 65895 65684 0 09:59 pts/3 00:00:00 grep --color=auto ping
]# ls -la /proc/65890/fd/[0-2]
lr-x------ 1 root root 64 Aug 20 09:58 /proc/65890/fd/0 -> pipe:[651007]
l-wx------ 1 root root 64 Aug 20 09:58 /proc/65890/fd/1 -> pipe:[651008]
l-wx------ 1 root root 64 Aug 20 09:58 /proc/65890/fd/2 -> pipe:[651009]
now, instead of seeing file descriptors like "/dev/pts/xxx" we are seeing something weird like pipe:[xxx]
. What is that? That is UNIX IPC
- child process sends data to parent process via anonymous pipe. Anonymous pipes are not tty
s, and that is why nohup
wrapper
won't have any effect, we may demonstrate that using shell
(neither "nohup.out" in output nor "ignoring input and appending output to 'nohup.out'" message):
]# nohup ping 8.8.8.8 </dev/null 2>&1 | cat
^Z
[1] Stopped nohup ping 8.8.8.8 < /dev/null 2>&1 | cat
]# ps -ef | grep ping
root 66840 65684 0 12:38 pts/3 00:00:00 ping 8.8.8.8
root 66845 65684 0 12:38 pts/3 00:00:00 grep --color=auto ping
]# ls -la /proc/66840/fd/[0-2]
lr-x------ 1 root root 64 Aug 20 12:38 /proc/66840/fd/0 -> /dev/null
l-wx------ 1 root root 64 Aug 20 12:38 /proc/66840/fd/1 -> pipe:[659814]
l-wx------ 1 root root 64 Aug 20 12:38 /proc/66840/fd/2 -> pipe:[659814]
What will happen if parent process gets exited? Actually, it depends on whether child process is sending data via anonymous pipe or not, but in the worst case child process will exit:
]# kill 65873
]# ls -la /proc/65890/fd/[0-2]
]# ps -ef | grep ping
root 66238 65684 0 10:31 pts/3 00:00:00 grep --color=auto ping
And the only reliable way to get the behaviour similar to nohup
wrapper
is to "re-implement" nohup
wrapper
on Java side:
import java.io.File;
public class Test {
public static void main(String[] args) throws Exception {
Process process = new ProcessBuilder("ping", "8.8.8.8")
.directory(new File("/tmp"))
.redirectInput(new File("/dev/null"))
.redirectOutput(new File("/tmp/nohup.out"))
.redirectError(new File("/tmp/nohup.out"))
.start();
Thread.sleep(1_000_000_000);
}
}
]# ps -ef | grep ping
root 66394 66377 0 10:37 pts/2 00:00:00 ping 8.8.8.8
root 66398 65684 0 10:37 pts/3 00:00:00 grep --color=auto ping
]# kill 66377
]# ls -la /proc/66394/fd/[0-2]
lr-x------ 1 root root 64 Aug 20 10:37 /proc/66394/fd/0 -> /dev/null
l-wx------ 1 root root 64 Aug 20 10:37 /proc/66394/fd/1 -> /tmp/nohup.out
l-wx------ 1 root root 64 Aug 20 10:37 /proc/66394/fd/2 -> /tmp/nohup.out
]# ps -ef | grep 66394
root 66394 1 0 10:37 pts/2 00:00:00 ping 8.8.8.8
root 66402 65684 0 10:38 pts/3 00:00:00 grep --color=auto 66394
Thus, the java implementation of yours:
nohup java -jar /root/xxxx.jar &
is:
Process process = new ProcessBuilder("java", "-jar", "/root/xxxx.jar")
.redirectInput(new File("/dev/null"))
.redirectOutput(new File("nohup.out"))
.redirectError(new File("nohup.out"))
.start();