【问题标题】:Windows overlapped IO actually blocksWindows 重叠 IO 实际上会阻塞
【发布时间】:2017-10-12 21:04:42
【问题描述】:

我正在尝试 Windows 重叠 IO,但我似乎无法让它异步工作。我已经编译并运行了下面的程序,但它从不打印任何东西,它只是默默地完成。我读过小读可能会同步,所以我特意选择读512MB。

  const DWORD Size = 1<<29; // 512MB
  char* Buffer = (char*)malloc(Size);
  DWORD BytesRead;
  OVERLAPPED Overlapped;
  memset(&Overlapped, 0, sizeof(Overlapped));

  HANDLE File = CreateFile("BigFile", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, NULL);
  assert(File!=INVALID_HANDLE_VALUE);

  DWORD Result = ReadFileEx(File, Buffer, Size, &Overlapped, NULL); // This line takes 150ms according to the debugger
  assert(Result);

  while(!GetOverlappedResult(File, &Overlapped, &BytesRead, FALSE)) {
    printf("Waiting...\n");
  }

作为附加信息,我已将代码单步执行到调试器中,Overlapped.InternalHigh 值在ReadFileEx 调用期间得到更新(与Size 具有相同的值)。

我尝试将malloc 替换为VirtualAlloc,将ReadFileEx 替换为ReadFile,添加FILE_FLAG_NO_BUFFERING,并检查ReadFile 的返回值为0,GetLastErrorERROR_IO_PENDING读完之后。我尝试使用 RAMMap 来查看文件是否在缓存中,但其中只有 96KB。

我正在运行具有 8GB RAM 的 Windows 10(版本 1703)。

【问题讨论】:

  • 也许文件被操作系统缓存了,所以操作系统决定它可以轻松地立即为读取服务?
  • @EOF 会严重缓存整个 512MB 以上的文件吗?另外,如果它被缓存,我的读取仍然需要 150 毫秒......那么如果它不能保证我快速返回,那么重叠的意义何在?
  • 这确实与缓存相关。使用FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING 打开文件并分配PVOID Buffer = VirtualAlloc(0, Size, MEM_COMMIT, PAGE_READWRITE)(必须对齐以进行非缓存读取)-并检查结果。在您的情况下,最好使用ReadFile(不带Ex)-在这种情况下,它会返回false,最后一个错误-ERROR_IO_PENDING
  • 我很确定如果有足够的 RAM 可用,它会缓存一个 512MB+ 的文件。您可以尝试在测试前使用 SysInternals RAMMap 清除缓存。
  • 奇怪... 150 毫秒对于排队异步 IO 请求来说太长了,是的,我同意这是应该发生的:(

标签: c windows winapi overlapped-io


【解决方案1】:

好的,感谢@RbMm。

除了FILE_FLAG_NO_BUFFERING 标志和ReadFile 的使用使其工作之外,内存分配没有任何改变。我尝试在ReadFileEx 之后使用SleepEx,它在0 处引发了访问冲突,这证明了@RbMm 的观点,即lpCompletionRoutine 不是可选的而是强制性的。 (对我来说,这意味着我将使用ReadFile,因为我不想要完成例程)

至于为什么我花了这么长时间才意识到发生了什么:我太信任调试器了,显然闯入调试器并没有停止 IO 进程,这意味着内存仍在OVERLAPPED 结构内更新,这让我觉得事情是瞬间的。最重要的是,我预计ReadFile 会很快返回,但如果我尝试读取 512MB,它实际上需要 20 毫秒,当我请求较小的量时它会快得多。

感谢大家的建议:)

为了完整起见,以下是工作程序:

  const DWORD Size = 1<<20;
  char* Buffer = (char*)malloc(Size);
  DWORD BytesRead;
  OVERLAPPED Overlapped;
  memset(&Overlapped, 0, sizeof(Overlapped));

  HANDLE File = CreateFile("BigFile", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING|FILE_FLAG_OVERLAPPED, NULL);
  assert(File!=INVALID_HANDLE_VALUE);
  DWORD Result = ReadFile(File, Buffer, Size, NULL, &Overlapped);
  assert(Result==FALSE && GetLastError()==ERROR_IO_PENDING);
  while(!GetOverlappedResult(File, &Overlapped, &BytesRead, FALSE)) {
    printf("Waiting...\n");
  }

【讨论】:

  • 您可以使用和ReadFileEx - 最终结果和操作时间没有什么不同,但我刚才发现lpCompletionRoutine 是强制性参数,而不是可选参数。它总是必须指向正确的(可以为空的)例程。否则,如果在 alertable 模式下成功调用 ReadFileEx 后将等待,则会崩溃。所以ReadFileEx(File, Buffer, Size, &amp;Overlapped, NULL); 是错误的。对于演示,在末尾添加下一个代码if (Result) SleepEx(INFINITE, TRUE);。在你完成GetOverlappedResult 之后
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-01
  • 2012-02-07
  • 2012-09-03
  • 1970-01-01
  • 1970-01-01
  • 2010-11-17
相关资源
最近更新 更多