【问题标题】:What happens when the filesystem can't keep up with output from bash redirection?当文件系统跟不上 bash 重定向的输出时会发生什么?
【发布时间】:2013-10-29 20:11:04
【问题描述】:

假设我有一些进程my_proc 会生成输出。我在 bash 中使用 > 将该输出重定向到文件,如下所示:

./my_proc > /some/file

当文件系统跟不上my_proc 的输出时会发生什么(即my_proc 生成输出的速度快于写入磁盘的速度)?我假设文件系统会做一些缓冲,但如果它永远赶不上怎么办?

有没有办法配置最大缓冲区大小?

对我来说,最佳解决方案是在缓冲区溢出时开始丢弃输出(开始重定向到/dev/null 或其他东西)。有没有一种简单的方法可以用 bash 做到这一点?

【问题讨论】:

    标签: bash pipe


    【解决方案1】:

    只要文件系统赶上,您的应用写入调用就会延迟。最有可能的净效应是您的应用程序在文件系统上等待时运行速度变慢。

    除非使用适当的标志打开目标文件,否则写入调用通常由 OS IO 子系统缓冲。但标准输出并非如此。可以使用适当的选项安装文件系统以禁用缓冲(即同步模式),这样可以避免缓冲,但出于性能原因通常不会这样做。

    为了得到你想要的,你需要对你的应用程序进行编程以缓冲输出,如果它检测到文件系统正在减慢你的速度,则丢弃缓冲区。但这没有任何意义。如果需要输出,则需要等待。如果你不需要它,那么最好不要一开始就写它。

    【讨论】:

    【解决方案2】:

    我认为@akostadinov 的回答是正确的。这可以通过一个简单的示例轻松说明:

    $ time seq 1 1000000
    1
    2
    ...
    999999
    1e+06
    
    real    0m40.817s
    user    0m0.600s
    sys     0m0.510s
    $ time seq 1 1000000 > file.txt
    
    real    0m0.556s
    user    0m0.540s
    sys     0m0.020s
    $ time seq 1 1000000 > /dev/null
    
    real    0m0.546s
    user    0m0.540s
    sys     0m0.000s
    $
    

    我们使用seq 实用程序输出数字 1 到 1000000,并将输出重定向到各个位置:

    • 没有重定向(输出到标准输出/终端),seq 运行速度要慢很多倍
    • 重定向到 /dev/null 和真正的文件相当接近,但重要的是,对于 /dev/null 版本,所用时间的“sys”部分为零。

    【讨论】:

      【解决方案3】:

      在 bash 中没有简单的方法可以做到这一点,但你可以在 C 中做到这一点。但首先,也许你可以不写每 N 行?要仅将第 100 行写入文件,您可以:

      slowprogram | sed -n '1~100p' > file
      

      无论如何,让我们使用 C sn-p 实现真正的非阻塞。由于 at 的作用类似于缓冲区但实际上并非如此,我们可以将其称为 bluffer.c

      #include <sys/types.h>
      #include <sys/stat.h>
      #include <fcntl.h>
      
      #define BUFFER_SIZE 4096
      
      int main(int argc, char** argv) {
        int out;
        char buffer[BUFFER_SIZE];
        int c;
      
        out = open("/dev/stdout", O_NONBLOCK | O_APPEND | O_WRONLY);
      
        while((c = read(0, buffer, BUFFER_SIZE)) != 0) {
            write(out, buffer, c);
        }
      }
      

      现在考虑一个快速生成一百万行 (~6.8MB) 数据的命令:

      time printf "%s\n" {1..1000000} > /dev/null
      
      real    0m1.278s
      

      现在让我们通过pv 将其速率限制为 1MB/s 来模拟慢速 IO:

      time printf "%s\n" {1..1000000} | pv -q -L 1M > slowfile
      
      real    0m7.514s
      

      正如预期的那样,它需要更长的时间,但slowfile 包含所有 1,000,000 行。

      现在让我们插入虚张声势:

      time printf "%s\n" {1..1000000} | ./bluffer | pv -q -L 1M > fastfile
      
      real    0m1.972s
      

      这次又很快结束了,fastfile 仅包含 1,000,000 行中的 141,960 行。在该文件中,我们看到如下所示的空白:

      52076
      52077
      188042
      188043
      

      【讨论】:

      • 如果我没看错这个答案,在读写模式下打开命名管道会隐式设置 O_NONBLOCK 标志:stackoverflow.com/questions/179291/…。我觉得这可以被利用来在 shell 中做我们想要的事情,而不必求助于自定义 c 程序,尽管对于我来说,我还无法弄清楚如何
      • 我很高兴听到对这种方法有用的任何信息。在我看来,有人会在阅读日志后大发雷霆,试图了解出了什么问题。
      • @DigitalTrauma 它只是说在读+写模式下打开会阻塞读取而不是只读的打开调用。
      • @akostadinov 我们只能推测。也许程序会写入大量进度信息,这些信息看起来不错,但不值得通过写入慢速 fbcon 来减慢系统速度。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-12-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多