0

当使用GetOverlapedResult获取重叠(即异步)I/O 操作的结果时,您可以要求GetOverlappdResult 等待”

DWORD le = ERROR_SUCCESS; //lastError = 0

if (!ReadFile(FSourceDiskHandle, buffer, BUFFER_SIZE, out bytesRead, overlapped)
{
   //The read operation did not complete synchronously. See if it's still pending.
   le = GetLastError;
   if (le == ERROR_IO_PENDING)
   {
      le = ERROR_SUCCESS;
      if (!GetOverlappedResult(FSourceDiskHandle, overlapped, out bytesRead, true) // <---bWait = true 
         le = GetLastError;
   }

   if (le != ERROR_SUCCESS)
      LogFmt("Error reading source: %s (%d)', SysErrorMessage(le), le], TRACE_LEVEL_ERROR);
}

这里要注意的部分是 : 的最后一个参数GetOverlappedResultbWait

bWait

如果此参数为TRUE,并且lpOverlapped结构的内部成员为STATUS_PENDING,则该函数在操作完成之前不会返回。如果此参数为FALSE且操作仍处于挂起状态,则函数返回FALSE并且GetLastError函数返回ERROR_IO_INCOMPLETE

对于我的代码,这意味着:

  • 如果 I/O 快速完成,ReadFile返回true
  • 如果 I/O 仍然挂起,那么我等待它完成
  • 如果还有其他错误,我会收到该错误。

这一切都很好。

大多数情况下,这一切都很好

我遇到了实际ReadFile操作需要十分钟才能返回的问题。当它返回时,它返回:

The device is not ready (21)

这是这家供应商的存储系统的一个众所周知的问题。.

我想做的是使用 Windows 的异步功能等待 - 但有一个超时。

我注意到GetOverlappedResultEx,它有一种超时参数:

BOOL GetOverlappedResultEx(
  HANDLE       hFile,
  LPOVERLAPPED lpOverlapped,
  LPDWORD      lpNumberOfBytesTransferred,
  DWORD        dwMilliseconds, <----------
  BOOL         bAlertable
);

但是当我查看文档时,这是我了解我不理解的 Windows 编程细节的地方 - 排队的 APC,可变等待。但它仍然听起来像我想要的::

dwMilliseconds

超时间隔,以毫秒为单位。

如果dwMilliseconds为零并且操作仍在进行中,则该函数立即返回并且 GetLastError 函数返回 ERROR_IO_INCOMPLETE

如果dwMilliseconds为非零且操作仍在进行中,则该函数将一直等待,直到对象发出信号、I/O 完成例程或 APC 排队,或者在返回之前经过了间隔。使用 GetLastError 获取扩展的错误信息。

如果dwMillisecondsINFINITE,则该函数仅在对象发出信号或 I/O 完成例程或 APC 排队时返回。

所以我尝试改变我的功能:

DWORD le = ERROR_SUCCESS; //lastError = 0

if (!ReadFile(FSourceDiskHandle, buffer, BUFFER_SIZE, out bytesRead, overlapped)
{
   //The read operation did not complete synchronously. See if it's still pending.
   le = GetLastError;
   if (le == ERROR_IO_PENDING)
   {
      le = ERROR_SUCCESS;
      //if (!GetOverlappedResult(FSourceDiskHandle, overlapped, out bytesRead, true) // <---bWait = true 
      if (!GetOverlappedResultEx(FSourceDiskHandle, overlapped, out bytesRead, 5000, False) //wait 5000 ms
         le = GetLastError;
   }

   if (le != ERROR_SUCCESS)
      LogFmt("Error reading source: %s (%d)', SysErrorMessage(le), le], TRACE_LEVEL_ERROR);
}

除了对GetOverlappedResultEx的调用没有在 5 秒(5,000 毫秒)内返回。相反,存储子系统需要 10 到 20 分钟才能恢复故障。

所以我随机尝试

我看到GetOverlappedResultsEx的另一个参数:

`bAlertable`

If this parameter is **TRUE** and the calling thread is in the waiting state, the function returns when the system queues an I/O completion routine or APC. The calling thread then runs the routine or function. Otherwise, the function does not return, and the completion routine or APC function is not executed.

A completion routine is queued when the [ReadFileEx][5] or [WriteFileEx][5] function in which it was specified has completed. The function returns and the completion routine is called only if *bAlertable* is **TRUE**, and the calling thread is the thread that initiated the read or write operation. An APC is queued when you call [QueueUserAPC][5].

听起来不像我的情况:

  • 我没有使用ReadFileEx
  • 我不打电话QueueUserAPC

但这并不一定会阻止我随机尝试事物并希望它们起作用:

DWORD le = ERROR_SUCCESS; //lastError = 0

if (!ReadFile(FSourceDiskHandle, buffer, BUFFER_SIZE, out bytesRead, overlapped)
{
   //The read operation did not complete synchronously. See if it's still pending.
   le = GetLastError;
   if (le == ERROR_IO_PENDING)
   {
      le = ERROR_SUCCESS;
      //if (!GetOverlappedResult(FSourceDiskHandle, overlapped, out bytesRead, true) // <---bWait = true 
      if (!GetOverlappedResultEx(FSourceDiskHandle, overlapped, out bytesRead, 5000, False) //wait 5000 ms
      if (!GetOverlappedResultEx(FSourceDiskHandle, overlapped, out bytesRead, 5000, True) //wait 5000 ms, alertable

         le = GetLastError;
   }

   if (le != ERROR_SUCCESS)
      LogFmt("Error reading source: %s (%d)', SysErrorMessage(le), le], TRACE_LEVEL_ERROR);
}

但它不起作用。

所以我问 Stackoverflow

我可以使用超时模拟同步操作ReadFile吗?GetOverlappedResultEx

我敢肯定我最终会得到一个涉及消息传递计时器的错误黑客(或者是线程计时器?或者是警报计时器?和事件?和我自己的事件?或者重叠的事件?)。但我宁愿使用好的解决方案而不是我的解决方案。

4

1 回答 1

0

我可以ReadFile使用超时模拟同步操作GetOverlappedResultEx吗?

是的,你可以,就像你一样,已经尝试过了。这不是模拟。这将是完全同步的文件读取。因为同步读取 - 这是异步读取 + I/O 完成时就地等待。所以代码可以是下一个:

ULONG ReadFileTimeout(HANDLE hFile, 
               PVOID lpBuffer, 
               ULONG nNumberOfBytesToRead, 
               PULONG lpNumberOfBytesRead, 
               PLARGE_INTEGER ByteOffset, 
               ULONG dwMilliseconds)
{
    OVERLAPPED ov;
    ov.Offset = ByteOffset->LowPart;
    ov.OffsetHigh = ByteOffset->HighPart;

    ULONG dwError = NOERROR;

    if (ov.hEvent = CreateEvent(0, 0, 0, 0))
    {
        dwError = ReadFile(hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, &ov) ? NOERROR : GetLastError();

        if (dwError == ERROR_IO_PENDING)
        {
            dwError = GetOverlappedResultEx(0/*yes, not need hFile*/, 
                &ov, lpNumberOfBytesRead, dwMilliseconds, FALSE) ? NOERROR : GetLastError();
        }

        CloseHandle(ov.hEvent);
    }
    else
    {
        dwError = GetLastError();
    }

    return dwError;
}

请注意,您必须(在异步文件的情况下)始终显式使用字节偏移量,从中读取数据。

GetOverlappedResultEx通常下一步 -检查- I / O是否完成(OVERLAPPED只需Internal与 如果发出信号(从返回)决定 I/O 完成并从返回结果,否则返回适当的错误。STATUS_PENDINGInternal == STATUS_PENDINGWaitForSingleObjectExhEventOVERLAPPEDdwMillisecondsbAlertablehEventWAIT_OBJECT_0WaitForSingleObjectExGetOverlappedResultExOVERLAPPED

但万一它ReadFile本身没有返回控制权并等待 - 这里不可能做点什么。这意味着驱动程序开始等待您的线程。并且没有任何办法打破这个司机的等待。看起来这正是你的情况。当您使用异步文件时-驱动程序不能等待,但是..有时(写得不好的驱动程序)这可能是

于 2020-01-06T11:45:48.157 回答