You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过别名终止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>':获取匹配进程的PID
  • pkill -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());
    }
}

关键注意事项

  1. Docker环境权限:容器内进程通常以root运行,确保Java进程有足够权限执行pkill、kill命令
  2. 信号兼容性:如果urlworker.sh未处理-TERM信号,可直接使用-9强制杀死,但优先推荐优雅终止以避免资源泄漏
  3. 命令执行规范:所有带管道、变量替换的shell命令必须通过/bin/sh -c包裹,否则Java无法正确解析执行

内容的提问来源于stack exchange,提问作者Magno C

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 08:25:37