Java Process的一些小坑
背景
在图片或者视频处理, 或者其他需求,需要调用其他工具做些操作或者处理(如调用shell,shell 里面在调用其他子进程)等, 用Java的Process来调用其他进程处理, 当使用process.destroyForcibly()
时, 会有一些坑在, 父进程被kill, 子进程未被kill,导致存在过多僵死进程,造成机器过载, 影响服务。
实例分析
Process.destroyForcibly代码片段
//UNIXProcess
@Override
public Process destroyForcibly() {
destroy(true);
return this;
}
对应底层native代码
JNIEXPORT void JNICALL
Java_java_lang_UNIXProcess_destroyProcess(JNIEnv *env,
jobject junk,
jint pid,
jboolean force)
{
int sig = (force == JNI_TRUE) ? SIGKILL : SIGTERM;
kill(pid, sig);
}
从底层C代码,可以看到, 当使用force时, 发送的中SIGKILL
也就是-9
强杀, 这个信号是不能被捕捉的(内核的安全机制原因)。SIGTERM
也是终止进程的信号, 但可以被阻塞、处理和忽略。
解决
在process.destroyForcibly()之前做下cleanup工作。
cleanup的工作是拿到父进程, 再kill所有的子进程
//kill sub processes
private void cleanup(Process p) {
int pid = ProcessUtils.getPid(p);
if (pid != -1) {
try {
Process cleanUpProcess = new ProcessBuilder("/usr/bin/pkill", "-9", "-P", String.valueOf(pid)).start();
int exitcode = cleanUpProcess.waitFor();
ProcessLogger.warn("CLEANUP", String.format("pid: %d exitcode: %d", pid, exitcode));
} catch (Exception e) {
ProcessLogger.warn("CLEANUP pid: " + pid, e);
}
} else {
ProcessLogger.warn("CLEANUP", "get pid error");
}
}
public static synchronized int getPid(Process p) {
int pid = -1;
try {
if (p.getClass().getName().equals("java.lang.UNIXProcess")) {
Field f = p.getClass().getDeclaredField("pid");
f.setAccessible(true);
pid = f.getInt(p);
f.setAccessible(false);
}
} catch (Exception e) {
pid = -1;
}
return pid;
}
**说明:**获取pid
的方式, 在JDK9之前只能通过反射机制, 在JDK9可以直接通过接口拿到。
题外话
如果是直接通过脚本处理, 可以通过trap
+pkill
, 捕捉信息做清理工作
cleanup() {
#do clean up
>&2 echo "interrupted!"
exit 2
}
trap cleanup SIGHUP SIGINT SIGQUIT SIGTERM
Read other posts