Skip to content

[Bug]: Shell命令执行工具在执行 cat等输出较大指令时频繁超时 (Process Deadlock) #617

@devotion-coding

Description

@devotion-coding

Describe the bug
在调用 executeCommand 方法执行简单的 Shell 命令(如 cat)时,如果命令输出的内容稍多(超过系统管道缓冲区),执行过程会卡住直到触发超时,导致即使成功的命令也返回“TimeoutError”。

To Reproduce
Steps to reproduce the behavior:

  1. agent使用ShellCommonTool cat 一个大文件
  2. See error “TimeoutError: The command execution exceeded the timeout of ...”

Environment (please complete the following information):

  • AgentScope-Java Version: 1.0.7
  • Java Version: 17
  • OS: 代码问题,平台无关

Additional context
问题描述:
在调用 executeCommand 方法执行简单的 Shell 命令(如 cat)时,如果命令输出的内容稍多(超过系统管道缓冲区),执行过程会卡住直到触发超时,导致即使成功的命令也返回“TimeoutError”。

受影响范围:
所有通过 executeCommand 执行且输出内容 > 系统管道缓冲区(通常为 4KB - 64KB)的命令。

环境信息:

相关类/方法: ShellCommandTool.executeCommand()
复现步骤:

准备一个文本文件 test.txt,内容大小约为 20KB(足以填满默认缓冲区)。
调用工具方法:executeCommand("cat test.txt", 10) (设置超时为 10 秒)。
观察日志或返回结果。
预期结果: 命令在毫秒级完成,返回文件内容。
实际结果: 命令阻塞约 300 秒,最终抛出/返回 TimeoutError,提示“exceeded timeout of 300 seconds”。
根本原因分析:
该问题由 Java Process 缓冲区死锁 引起。当前代码逻辑如下:

processBuilder.start() 启动子进程。
调用 process.waitFor(...) 阻塞等待进程结束。
进程结束后,调用 readStream(...) 读取输出。
死锁机制:
当子进程(如 cat)向标准输出写入数据时,操作系统使用固定大小的管道缓冲区。如果写入的数据填满了该缓冲区,子进程会被挂起,等待父进程读取数据腾出空间。
然而,父进程此时正阻塞在 waitFor() 中等待子进程结束。

子进程:等父进程读数据。
父进程:等子进程结束。
结果:互相等待 -> 死锁 -> 直到超时强制终止。
建议修复方案:
必须在调用 process.waitFor() 之前 或 同时,在独立线程中消耗 InputStream(标准输出)和 ErrorStream(标准错误流),以防止缓冲区填满。

具体代码修改建议:

在 process.start() 之后,立即启动两个后台线程分别读取 getInputStream() 和 getErrorStream()。
将读取到的内容暂存到 StringBuilder 或线程安全的容器中。
主线程调用 process.waitFor(...)。
waitFor 返回后,合并后台线程读取到的内容并返回。
参考伪代码:

java

// 1. 启动进程
process = processBuilder.start();

// 2. 启动线程异步读取流 (防止死锁)
Thread stdOutThread = new Thread(() -> readStream(process.getInputStream(), ...));
Thread stdErrThread = new Thread(() -> readStream(process.getErrorStream(), ...));
stdOutThread.start();
stdErrThread.start();

// 3. 等待进程结束
boolean completed = process.waitFor(timeoutSeconds, TimeUnit.SECONDS);

// 4. 等待读取线程完成并获取结果
stdOutThread.join();
stdErrThread.join();
// ...
附件:

Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions