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

如何列出被org.apache.commons.exec.ExecuteWatchDog终止的进程?

记录被ExecuteWatchDog销毁的进程方法

因为org.apache.commons.exec.ExecuteWatchDog本身没有提供直接记录被销毁进程的API,你可以通过以下两种方式实现需求:

方法一:扩展ExecuteWatchDog类(推荐)

重写WatchDog的destroyProcess方法——这个方法是WatchDog销毁超时进程时的核心调用点,在里面添加进程信息记录逻辑即可。

自定义追踪版WatchDog代码

import org.apache.commons.exec.ExecuteWatchDog;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.lang.reflect.Field;

public class TrackingExecuteWatchDog extends ExecuteWatchDog {
    private final List<ProcessRecord> destroyedProcesses = Collections.synchronizedList(new ArrayList<>());
    private String command;

    public TrackingExecuteWatchDog(long timeout) {
        super(timeout);
    }

    // 设置要执行的命令,用于记录
    public void setCommand(String command) {
        this.command = command;
    }

    @Override
    protected void destroyProcess(Process process) {
        // 先执行父类的销毁逻辑
        super.destroyProcess(process);
        
        // 获取进程PID(兼容JDK8及以上)
        long pid = getProcessPid(process);
        
        // 记录销毁信息
        destroyedProcesses.add(new ProcessRecord(pid, command, LocalDateTime.now()));
    }

    // 提供外部获取被销毁进程列表的方法
    public List<ProcessRecord> getDestroyedProcesses() {
        return new ArrayList<>(destroyedProcesses);
    }

    // 内部类存储进程销毁记录
    public static class ProcessRecord {
        private final long pid;
        private final String command;
        private final LocalDateTime destroyTime;

        public ProcessRecord(long pid, String command, LocalDateTime destroyTime) {
            this.pid = pid;
            this.command = command;
            this.destroyTime = destroyTime;
        }

        // getter方法
        public long getPid() { return pid; }
        public String getCommand() { return command; }
        public LocalDateTime getDestroyTime() { return destroyTime; }
    }

    // 私有方法:获取进程PID
    private long getProcessPid(Process process) {
        // JDK9+ 直接用ProcessHandle
        if (process instanceof java.lang.ProcessHandle) {
            return ((java.lang.ProcessHandle) process).pid();
        }
        
        // JDK8及以下用反射获取PID
        try {
            Field pidField = process.getClass().getDeclaredField("pid");
            pidField.setAccessible(true);
            return pidField.getLong(process);
        } catch (Exception e) {
            return -1; // 标记无法获取PID
        }
    }
}

使用方式

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import java.io.IOException;
import java.util.List;

public class ExecDemo {
    public static void main(String[] args) throws IOException, InterruptedException {
        // 定义要执行的命令
        CommandLine cmdLine = CommandLine.parse("sleep 60");
        
        // 初始化自定义WatchDog,设置30秒超时
        TrackingExecuteWatchDog watchDog = new TrackingExecuteWatchDog(30000);
        watchDog.setCommand(cmdLine.toString());
        
        // 关联到DefaultExecutor
        DefaultExecutor executor = new DefaultExecutor();
        executor.setWatchdog(watchDog);
        
        // 执行命令
        try {
            executor.execute(cmdLine);
        } catch (Exception e) {
            // 处理执行异常(超时会抛出ExecuteException)
        }
        
        // 查看被销毁的进程记录
        List<TrackingExecuteWatchDog.ProcessRecord> destroyed = watchDog.getDestroyedProcesses();
        for (TrackingExecuteWatchDog.ProcessRecord record : destroyed) {
            System.out.printf("被销毁进程:PID=%d,命令=%s,销毁时间=%s%n",
                    record.getPid(), record.getCommand(), record.getDestroyTime());
        }
    }
}

方法二:通过ExecuteResultHandler回调判断

利用ExecuteResultHandler的失败回调,结合ExecuteWatchDog#killedProcess()方法判断是否是超时销毁,再记录进程信息。

代码示例

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteException;
import org.apache.commons.exec.ExecuteResultHandler;
import org.apache.commons.exec.ExecuteWatchDog;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ExecResultHandlerDemo {
    public static void main(String[] args) throws InterruptedException {
        CommandLine cmdLine = CommandLine.parse("sleep 60");
        long timeout = 30000;
        ExecuteWatchDog watchDog = new ExecuteWatchDog(timeout);
        
        List<ProcessRecord> destroyedProcesses = Collections.synchronizedList(new ArrayList<>());
        
        DefaultExecutor executor = new DefaultExecutor();
        executor.setWatchdog(watchDog);
        
        ExecuteResultHandler resultHandler = new ExecuteResultHandler() {
            @Override
            public void onProcessComplete(int exitValue) {
                // 进程正常结束,无需记录
            }

            @Override
            public void onProcessFailed(ExecuteException e) {
                // 判断是否是WatchDog销毁的进程
                if (watchDog.killedProcess()) {
                    long pid = getProcessPid(e.getProcess());
                    destroyedProcesses.add(new ProcessRecord(pid, cmdLine.toString(), LocalDateTime.now()));
                }
            }
        };
        
        // 异步执行命令
        try {
            executor.execute(cmdLine, resultHandler);
        } catch (Exception e) {
            // 处理提交异常
        }
        
        // 等待执行完成
        resultHandler.waitFor();
        
        // 输出记录
        for (ProcessRecord record : destroyedProcesses) {
            System.out.printf("被销毁进程:PID=%d,命令=%s,销毁时间=%s%n",
                    record.getPid(), record.getCommand(), record.getDestroyTime());
        }
    }
    
    // 复用之前的getProcessPid方法
    private static long getProcessPid(Process process) {
        if (process instanceof java.lang.ProcessHandle) {
            return ((java.lang.ProcessHandle) process).pid();
        }
        try {
            Field pidField = process.getClass().getDeclaredField("pid");
            pidField.setAccessible(true);
            return pidField.getLong(process);
        } catch (Exception e) {
            return -1;
        }
    }
    
    private static class ProcessRecord {
        private final long pid;
        private final String command;
        private final LocalDateTime destroyTime;

        public ProcessRecord(long pid, String command, LocalDateTime destroyTime) {
            this.pid = pid;
            this.command = command;
            this.destroyTime = destroyTime;
        }

        public long getPid() { return pid; }
        public String getCommand() { return command; }
        public LocalDateTime getDestroyTime() { return destroyTime; }
    }
}

注意事项

  • PID兼容性:JDK9及以上可以直接用ProcessHandle获取PID,JDK8及以下需要通过反射(不同JVM实现可能略有差异,比如OpenJDK和OracleJDK的Process类PID字段名一致,但其他JVM可能不同)。
  • 线程安全:如果是多线程执行命令,要确保记录进程信息的集合是线程安全的(比如用Collections.synchronizedList或ConcurrentLinkedQueue)。
  • 命令信息传递:需要在执行前把命令字符串传递到记录逻辑中,避免无法关联进程对应的命令。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 04:47:08