【问题标题】:Win32 buffer size for read from stdin用于从标准输入读取的 Win32 缓冲区大小
【发布时间】:2019-08-21 10:09:26
【问题描述】:

我正在尝试在 Windows 上使用 C++ 高效地从标准输入读取数据,这意味着最好是大块。这可以通过以下方式完成:

ReadFile(GetStdHandle(STD_INPUT_HANDLE), buf, bytestoread, &bytesread, 0);

read(0, buf, bytestoread);

但在这两种情况下,它仅在 bytestoread 设置为非常小的数字时才有效,例如50;如果设置为更大的数字,例如一兆字节,调用失败并出现“空间不足”错误,好像数据没有直接进入我提供的缓冲区,而是通过一些固定大小的内部缓冲区复制。无论输入是通过管道传输还是在控制台上键入,都是如此。

Windows 是否只是限制了进程一次可以从标准输入读取的块大小?如果是这样,保证工作的最大块大小是多少?

显示问题的完整程序:

    #include <errno.h>
    #include <io.h>
    #include <stdio.h>
    #include <string.h>

    char buf[1000000];

    int main(int argc, char **argv) {
      auto r = read(0, buf, sizeof buf);
      if (r < 0)
        perror("read");
      return 0;
    }

【问题讨论】:

  • Windows 不受限制。但这是设备(到该点STD_INPUT_HANDLE)特定的。控制台、管道、文件系统文件的结果可能不同
  • @RbMm 好的,那么什么值可以保证在所有情况下都有效?
  • 这是一种不寻常的控制台输入方式,当然我们都想知道这样做的意义何在。但可以肯定的是,控制台输入是独立缓冲的,在您按下 Enter 键之前什么都不会发生。 64KB 是控制台的流行限制。当然,您必须修改 Get/SetConsoleMode(),并期望您至少必须关闭 ENABLE_LINE_INPUT 选项。
  • @rwallace 确实:一个兆字节的缓冲区,接受一行输入而不会出错。当我通过管道输入大量数据时,我会收到一个不同的错误,因为即使在管道关闭后,Windows 仍会尝试写入管道。那是因为我们实际上应该在一个循环中读取,直到我们读取所有数据——但这是一个完全不同的问题。我使用 Embarcadero C++Builder 编译,它使用不同的 C 运行时。我会看看是否可以尝试使用 Microsoft C++。
  • @rwallace 我现在使用 Visual Studio 2008 的编译器进行编译。 “echo hello | foo”有效,但现在只有“foo”说“阅读:没有足够的空间”!有趣的。 (我使用的是标准的 Windows 终端)。实际上,我的原始程序也发生了同样的情况:只有没有管道输入的程序说“读取:内存不足”。显然我以前没有尝试过。

标签: windows winapi stdin


【解决方案1】:

您没有说明您使用的是哪个版本的运行时和操作系统,但我无法在 Windows 10 上使用 MSVC 19.16.27031.1 重现此问题。有一些记录在案的原因可能会失败。来自MSDN documentation of ReadFile:

可以通过使用带有控制台输入句柄的ReadFile 从控制台输入缓冲区读取字符。控制台模式确定ReadFile 函数的确切行为。默认情况下,控制台模式是ENABLE_LINE_INPUT,这表示ReadFile 应该读取直到它到达一个回车。如果按 Ctrl+C,则调用成功,但 GetLastError 返回 ERROR_OPERATION_ABORTED。如需更多信息,请参阅CreateFile

还有另一种可能导致此错误的方式,与异步 I/O 相关,但这似乎不是问题所在。您可能想用SetConsoleMode 关闭ENABLE_LINE_INPUT 标志。该文档还说,如果无法锁定缓冲区的内存页面,则调用可能会失败并显示ERROR_NOT_ENOUGH_QUOTA。但是,您使用的静态缓冲区应该不会有这个问题。

如果您正在读取磁盘上的文件,而不是控制台流,您可以使用map it to memory,,它消除了任何中间缓冲并根据需要加载文件的各个部分,其机制与虚拟内存相同。

【讨论】:

  • ENABLE_LINE_INPUT 与问题有什么关系?
  • @RbMm 来自文档:“默认情况下,控制台模式是 ENABLE_LINE_INPUT,这表明 ReadFile 应该读取直到它到达回车。”这可能会导致更大的读取失败。
  • 不,我不认为这会如何导致更大的读取失败。这一切与问题有何关系
  • 文档还说,如果无法锁定缓冲区的内存页,调用可能会失败并显示 ERROR_NOT_ENOUGH_QUOTA。但是,您使用的静态缓冲区应该不会有这个问题。 这也是绝对错误的。在这里是缓冲区“静态”还是不是绝对无关的。所有用户模式缓冲区都是可分页的。问题可能是设备是否使用直接 io
  • 页面对齐缓冲区有帮助吗? - 没有
【解决方案2】:

如果您尝试读取 stdin binary 您需要的数据:

  1. 设置_setmode( _fileno( stdin), _O_BINARY);
  2. fread( buf, 1, bufSize, stdin)

看看我的类似解决方案 应用程序通过stdin、油门速度和 发给stdout

bin_pipe_throttle

【讨论】:

    【解决方案3】:

    据我所知,stdin 不限。它作为一个无尽的流工作,应该提供你需要的尽可能多的存储空间。我看到的唯一选择是您正在使用的内核在某些时候会阻塞

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-12
      • 2011-05-18
      • 1970-01-01
      • 2018-09-24
      • 1970-01-01
      • 2011-07-01
      • 2018-12-12
      • 1970-01-01
      相关资源
      最近更新 更多