为啥 InputStreamReader 不实时读取进程的输出?

Posted

技术标签:

【中文标题】为啥 InputStreamReader 不实时读取进程的输出?【英文标题】:Why InputStreamReader doesn't read process' output in real time?为什么 InputStreamReader 不实时读取进程的输出? 【发布时间】:2013-01-05 13:38:13 【问题描述】:

我正在尝试使用 ProcessBuilder 运行一些外部命令,例如 Linux 中的 ifstatvmstat

此类命令支持自定义采样间隔。如果我在外部命令中添加一个采样间隔,例如ifstat 20,那么命令将输出如下:

coolcfan@coolcfan-PC:~$ ifstat 20
       eth0               wlan0               vmnet1              vmnet8      
 KB/s in  KB/s out   KB/s in  KB/s out   KB/s in  KB/s out   KB/s in  KB/s out

20 秒后

   41.29      1.06      0.36      0.00      0.00      0.00      0.00      0.00

再过 20 秒

   16.67      0.58      0.38      0.00      0.00      0.00      0.00      0.00

然而,当我使用我的 Java 代码运行命令时,将在 20 秒后读取输出的第一部分,如下所示:

Start running "ifstat 20"

20 秒后

       eth0               wlan0               vmnet1              vmnet8      
 KB/s in  KB/s out   KB/s in  KB/s out   KB/s in  KB/s out   KB/s in  KB/s out
   66.61      1.73      1.29      0.01      0.00      0.00      0.00      0.00

当我使用 NIO 服务器运行命令并使用 SocketChannel 将输出发送到客户端时,问题更加严重...(我的客户端需要再等待 20 秒才能从服务器获取输出显示进程开始后 20 秒的第一个输出)

而且我注意到输出延迟的长度与我为命令设置的时间间隔有关。

那么为什么 ISR 不实时读取输出呢?

一个简单的测试代码sn-p来演示我的问题:

public static void main(String[] args) 
    ProcessBuilder pb = new ProcessBuilder();

    pb.command("ifstat 20".trim().split(" "));

    Process p = null;

    System.out.println("Start running \"ifstat 20\"");

    try 
        p = pb.start();

        char[] buf = new char[512];

        InputStreamReader isr = new InputStreamReader(p.getInputStream());

        int count = -1;

        while ((count = isr.read(buf, 0, buf.length)) != -1) 
            System.out.print(new String(buf, 0, count));
        
    
    catch (IOException ioe) 
    


更新:

正如彼得所说,这不是与 Java 相关的问题。这是一个管道延迟

但我还是不明白,为什么vmstat 20 | cat没有这个延迟,而ifstat 20 | cat会延迟显示头像?

【问题讨论】:

ISR 读取你得到的任何东西。为了比较,运行 ifstat 20 | cat 以便它写入管道而不是控制台。 @PeterLawrey 所以这是管道问题?其实我需要在现实环境中运行ifstat 120... 数据似乎没有延迟,只有标题。 @PeterLawrey 好的,好吧,是的.. 但是为什么头部延迟显示? 我怀疑它没有明确刷新。写入控制台时会自动刷新,但写入管道时需要显式刷新,直到出现第一行数据。 【参考方案1】:

确实如此。

延迟的是进程的输出。 STDIO 进行缓冲,根据 stout 是否为终端而有所不同。

【讨论】:

@coolcfan fflush() 在 C 程序中。你在外面无能为力。 您唯一能做的就是下载 ifstat 的源代码,在写入标头后添加一个 fflush() 并改用该版本。 应该注意的是,一些程序检测到一个 tty 并且在写入一个 tty 时比其他目的地(例如文件或管道)做更少的缓冲。即使输出不会发送到终端,您也可以创建一个伪 tty 以减少缓冲。 @EJP +1 for the "fflush() in the C program" 这是我寻找 8 小时终于开始工作的解决方案。仍然想知道它为什么有效?有什么诀窍?

以上是关于为啥 InputStreamReader 不实时读取进程的输出?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 InputStreamReader 从 jar 读取时会抛出 NPE?

Java基础InputStream InputStreamReader和BufferedReader

为啥不使用实时打包服务器进行生产?反应本机[关闭]

为啥这样写还是读不出来500数值,芯片是 24c02?

为啥我的 jQuery 代码不更新加载到视频元素中的实时图像?

为啥注册用户后 Firebase 实时数据库用户 ID 与 Firebase 身份验证 UID 不匹配?