即使为挂起的操作覆盖缓冲区,WriteFile 也会成功

Posted

技术标签:

【中文标题】即使为挂起的操作覆盖缓冲区,WriteFile 也会成功【英文标题】:WriteFile succeeded even when buffer is overwritten for pending operation 【发布时间】:2014-04-16 07:34:04 【问题描述】:

在我的程序中(发布到这个question),我在客户端做了以下更改:

wretry:   
   cbToWrite = _stprintf(chBuf[0], TEXT("Message %d from Client"), retrycount - numberofsend + 1);
   cbToWrite *= sizeof(TCHAR);

   fSuccess = WriteFile(hPipe, chBuf[0], cbToWrite,
  &cbWritten, &woverlapped[retrycount-numberofsend]);

这样,现在每次写入都会使用同一个缓冲区。

在这种情况下,WriteFile 在每次返回 ERROR_IO_PENDING 时都处于挂起状态。由于缓冲区的内容每次都会被覆盖,服务器应该接收到最后一条写入缓冲区的消息。但是服务器正在接收消息没有任何问题。

这是否意味着消息首先被复制到某个内部缓冲区中?我可以销毁缓冲区并期望消息被传递到服务器吗?

【问题讨论】:

【参考方案1】:

再次引用MSDN:

lpBuffer [in] 指向包含要写入文件或设备的数据的缓冲区的指针。 该缓冲区必须在写操作期间保持有效。在写入操作完成之前,调用者不得使用此缓冲区。

因此,如果您在写入操作发生时弄乱了缓冲区,似乎无法保证您的消息安全到达。你绝对不应该破坏缓冲区

【讨论】:

这是我所知道的。但就我而言,我收到的缓冲区完好无损。为什么我没有遇到问题? 在您再次访问缓冲区之前,您的写入操作很可能已完成(无法确定之前是什么),这隐藏了您的代码问题。此外,在任何现代语言中都强烈反对使用 goto 语句的代码 我为套接字编写了类似的代码。在那里我发现如果缓冲区被覆盖,则会传递不同的消息。因此,缓冲区应保留到操作挂起。【参考方案2】:

您的代码本质上是在使用未定义的行为。来自WriteFile 文档:

在写入操作使用缓冲区时访问输出缓冲区可能会导致从该缓冲区写入的数据损坏。在写入操作完成之前,应用程序不得写入、重新分配或释放写入操作正在使用的输出缓冲区。

正如 Raymond Chen 指出的那样,appearing to succeed is undefined behaviour

未定义的行为意味着任何事情都可能发生。该程序可能会立即崩溃。五分钟后它可能会崩溃。它可能会向你的老板发送电子邮件,说你搞砸了,然后给你读 Vogon 诗歌。或者可能不是。

您的代码可能现在可以工作,但它可能会在将来的某个时候停止工作。这可能是因为您已内置发布,或者 Microsoft 更改了 WriteFile 的底层工作方式或随机机会。

【讨论】:

如果微软的功能读到你 Vogon 的诗歌,我不会感到惊讶。看起来很合适。

以上是关于即使为挂起的操作覆盖缓冲区,WriteFile 也会成功的主要内容,如果未能解决你的问题,请参考以下文章

交换技术(swaping) 视频11

如何检测 Ansible playbook 在执行期间挂起的原因

etherscan-api 不输出挂起的交易

是否定义了跨操作系统睡眠/挂起的 setTimeout 行为?

Activiti7工作流引擎:挂起状态和任务组

Activiti7工作流引擎进阶