【问题标题】:Long delay hiccups for logging stdout to file将标准输出记录到文件的长时间延迟打嗝
【发布时间】:2015-06-22 19:41:25
【问题描述】:

我有一个 C 程序,每 10 毫秒将 3 行写入标准输出。如果我将输出重定向到文件(使用 > ),程序运行将有很长的延迟(60 毫秒)。延迟是周期性的(比如每 5 秒一次)。

如果我只是让它写入控制台或重定向到/dev/null,没有问题。

我怀疑这是标准输出缓冲区的问题,但使用 fflush(stdout) 并没有解决问题。

我该如何解决这个问题?

【问题讨论】:

  • 既然是用来做日志的,我可以用非阻塞文件来做这个吗?
  • linux 不是实时操作系统,延迟是不可避免的,尤其是写入磁盘时。
  • @user3386109 ,当然,但我想避免这种记录延迟。我已经从内核中屏蔽了 CPU 内核并将我的进程固定到内核。我现在的问题是这个 IO
  • 那么我建议使用共享内存将输出传输到在不同内核上运行的进程,然后让该进程将其写入磁盘。

标签: c performance


【解决方案1】:

如果我将输出重定向到一个文件(使用 > )会有很长的 程序运行延迟(60ms)。

这是因为当stdout 是终端设备时,它通常(虽然不是必需的)行缓冲,即在写入换行符时刷新输出缓冲区,而在常规文件的情况下,输出是完全缓冲的,这意味着当缓冲区已满或您关闭文件(或者您当然显式调用fflush())时,缓冲区会被刷新。

fflush(stdout) 对您来说可能还不够,因为它只会刷新标准 I/O 库缓冲区,但内核也会缓冲和延迟写入磁盘。调用fflush()后,可以在文件描述符上调用fsync(),将修改后的缓冲区缓存页刷新到磁盘,如fsync(STDOUT_FILENO)

小心,不要在没有调用fflush()之前就调用fsync()

更新:您也可以尝试sync(),与fsync() 不同,它不会阻塞等待底层写入返回。或者,正如另一个答案所建议的那样,fdatasync() 可能是一个不错的选择,因为它避免了更新文件时间的开销。

【讨论】:

  • 我测试了 fsync 但它使问题变得更糟,现在延迟每秒发生 20 次。 fsync 似乎有很长的延迟。
  • @Masood_mj 看到我的编辑 - 如果你想要更好的性能,你必须使用 fdatasync 并提前知道写入。实时高性能磁盘 I/O 是一项挑战。
  • @Masood_mj 嗯,可能是因为fsync() 阻塞,直到所有内容都被写入。请改用sync()(请参阅更新的答案)。
【解决方案2】:

您需要使用fsync。以下:

fsync(fileno(stdout))

应该有帮助。请注意,Linux 内核仍将根据其内部调度程序限制缓冲限制 I/O。以 root 身份运行并设置一个非常低的 nice 值可能会有所不同,如果您没有获得所需的频率。

如果仍然太慢,请尝试改用fdatasync。每个fflushfsync 都会导致文件系统更新节点元数据(文件大小、访问时间等)以及实际数据本身。如果您知道要写入多少数据块,那么您可以尝试以下技巧:

#define _XOPEN_SOURCE 500 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char **argv){

        FILE *fp = fopen("test.txt", "w");

        char *line = "Test\n";
        char *fill = "\0";

        fwrite(fill, 1, 100*strlen(line), fp);
        fflush(fp);
        fsync(fileno(fp));

        rewind(fp);

        for (int i = 0; i < 100; i++){

                fwrite(line, strlen(line), 1, fp);
                fflush(fp);
                fdatasync(fileno(fp));

        }

}

第一个 fwrite 调用将 5*100 个零写入一个块中的文件,然后 fsyncs 以便将其写入磁盘并更新节点信息。现在我们可以在不破坏文件系统元数据的情况下向文件写入多达 500 个字节。 rewind(3) 将文件指针位置返回到文件的开头,这样我们就可以在不改变节点文件大小的情况下覆盖数据。

该程序的时间安排如下:

$ time ./fdatasync
./fdatasync  0.00s user 0.01s system 1% cpu 0.913 total

因此它在 0.913 秒内运行 fdatasync 并同步到磁盘 100 次,平均每次写入和 fdatasync 调用约为 9 毫秒。

【讨论】:

  • 仍然 9 毫秒对我来说太多了。如果我的进程每 10 毫秒等待 9 毫秒,这意味着它几乎没有时间做任何其他事情。
  • @Masood_mj 这和一般答案一样好。大多数磁盘通常不会针对短时间内的大量小写入进行优化,大多数文件系统也没有针对这一点进行优化。在另一个线程中调用 fdatasync 可能会更好,但除此之外 - 解决方案将是更好的硬件 + 高性能文件系统 + 专门针对高 IOPS 调整的内核。
【解决方案3】:

可能只是每 5 秒您就填满了磁盘缓冲区,并且由于刷新到实际磁盘而导致延迟出现峰值。使用 iostat 检查

【讨论】:

  • 但如果是问题所在,有什么解决办法呢?我应该创建另一个进程/线程并将这些输出提供给它吗?
猜你喜欢
  • 2014-07-23
  • 2012-11-23
  • 1970-01-01
  • 2013-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-14
  • 2013-07-25
相关资源
最近更新 更多