如何通过别名终止ProcessBuilder创建的进程及其子进程?
解决方案:通过别名终止ProcessBuilder启动的进程及其子进程
核心问题分析
你之前的killByPid方法存在一个关键错误:Java的Runtime.exec不会自动启动shell解析管道(|)、变量替换等shell语法,直接传入带管道的命令字符串会导致命令执行失败,这是进程无法被杀死的主要原因。此外,你需要确保同时杀死目标脚本进程及其子进程(ffmpeg)。
方案一:基于别名匹配命令行(满足你的要求)
利用pkill的-f选项匹配包含别名的完整命令行,先杀死目标脚本进程,再杀死其子进程。注意必须通过/bin/sh -c来执行复杂shell命令:
public void stopWorker() { // 先尝试优雅终止,失败后强制杀死 killByAlias(true); if (this.process != null && this.process.isAlive()) { killByAlias(false); } } private void killByAlias(boolean graceful) { String sig = graceful ? "-TERM" : "-9"; String matchPattern = String.format("./urlworker.sh.*%s", this.streamName); try { // 1. 杀死匹配别名的urlworker.sh父进程 String killParentCmd = String.format("pkill -f '%s' %s", matchPattern, sig); Process p = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", killParentCmd}); p.waitFor(); // 2. 获取父进程PID,杀死其所有子进程(如ffmpeg) String getParentPidCmd = String.format("pgrep -f '%s'", matchPattern); Process pgrepProc = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", getParentPidCmd}); String parentPid = new String(pgrepProc.getInputStream().readAllBytes()).trim(); if (!parentPid.isEmpty()) { String killChildrenCmd = String.format("pkill -P %s %s", parentPid, sig); Process killChildProc = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", killChildrenCmd}); killChildProc.waitFor(); } } catch (Exception e) { logger.error("Failed to kill worker with alias: " + e.getMessage()); } }
原理说明:
pkill -f '<pattern>':匹配包含指定模式的完整命令行,精准定位你启动的urlworker.sh进程,避免误杀其他同类进程pgrep -f '<pattern>':获取匹配进程的PIDpkill -P <pid>:杀死指定PID的所有子进程- 优先使用
-TERM优雅终止,失败后再用-9强制杀死
方案二:基于已保存的Process对象(更可靠)
既然你已经将启动的进程保存到this.process,可以直接获取其PID,然后杀死该进程及其子进程,无需依赖别名:
import java.util.concurrent.TimeUnit; public void stopWorker() { if (this.process == null || !this.process.isAlive()) { return; } try { long parentPid = this.process.pid(); // 1. 优雅终止子进程 Process killChildProc = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", "pkill -P " + parentPid + " -TERM"}); killChildProc.waitFor(); // 2. 优雅终止父进程,超时则强制杀死 this.process.destroy(); if (!this.process.waitFor(5, TimeUnit.SECONDS)) { killChildProc = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", "pkill -P " + parentPid + " -9"}); killChildProc.waitFor(); this.process.destroyForcibly(); } } catch (Exception e) { logger.error("Failed to stop worker process: " + e.getMessage()); } }
关键注意事项
- Docker环境权限:容器内进程通常以root运行,确保Java进程有足够权限执行
pkill、kill命令 - 信号兼容性:如果
urlworker.sh未处理-TERM信号,可直接使用-9强制杀死,但优先推荐优雅终止以避免资源泄漏 - 命令执行规范:所有带管道、变量替换的shell命令必须通过
/bin/sh -c包裹,否则Java无法正确解析执行
内容的提问来源于stack exchange,提问作者Magno C
相关产品推荐
相关产品推荐

