使用 io 完成端口时,WriteFile 是不是会在完成时立即发布完成数据包
Posted
技术标签:
【中文标题】使用 io 完成端口时,WriteFile 是不是会在完成时立即发布完成数据包【英文标题】:does WriteFile post a completion packet on finishing immediately when using io completion ports使用 io 完成端口时,WriteFile 是否会在完成时立即发布完成数据包 【发布时间】:2019-11-22 19:09:59 【问题描述】:我知道当 WSARecv 与它一起使用时,它可能会立即完成,在这种情况下,仍然会发布完成数据包,并且可以使用 SetFileCompletionNotificationModes 更改此行为
但我想知道使用 WriteFile 的文件和管道的情况,如果我使用 iocp 进行异步读写并且操作立即完成,是否会发生相同的行为?还是我必须处理?
【问题讨论】:
这里的WSARecv
或WriteFile
没有任何区别。两者都是异步 i/o api
【参考方案1】:
每Why does my asynchronous I/O request return TRUE instead of failing with ERROR_IO_PENDING?:
当您指定 FILE_FLAG_OVERLAPPED 时,您保证您的程序知道如何处理异步完成的 I/O,但它不需要 I/O 堆栈以异步方式运行。无论如何,驱动程序都可以选择同步执行您的 I/O。例如,如果写入操作可以通过写入缓存而不阻塞来执行,驱动程序只会将数据复制到缓存并指示同步完成。别担心,开心就好:您的 I/O 完成速度比您预期的还要快!
即使 I/O 同步完成,所有异步完成通知机制仍处于活动状态。只是他们都在 WriteFile 调用返回之前完成了他们的工作。这意味着事件句柄仍将被发出信号,完成例程仍将运行(一旦您警觉地等待),并且如果句柄绑定到 I/O 完成端口,则 I/O 完成端口将收到完成通知。
您可以使用 SetFileCompletionNotificationModes 函数来更改此行为的某些方面,从而在潜在异步 I/O 请求同步完成时对 I/O 子系统的行为进行一些控制。
【讨论】:
谢谢。这包括包括 ConnectNamedPipe 在内的所有异步 API,对吧? 我的意思是如果 ConnectNamedPipe 返回 FALSE 并且 GetLastError() 返回 ERROR_PIPE_CONNECTED 是否会发布完成通知? @dev65 - 是的,会的。这与绑定到某个 iocp 的文件上的 any 异步 api 有关,以防我们将非零指针传递给上下文 @Remy 但从docs.microsoft.com/en-us/windows/win32/ipc/… 的示例中,当返回 ERROR_PIPE_CONNECTED 时,它们会发出一个事件信号 @dev65 per that example:“如果操作在函数返回之前完成,则事件对象的状态不会改变。” - 我不知道这是不是不管真假,我不经常使用重叠 I/O。似乎与雷蒙德所说的相矛盾。所以你只需要自己尝试一下,看看会发生什么。【参考方案2】:所有异步api都存在共同行为,在CancelThreadpoolIo函数中隐含描述
注意,根据设计,我们需要调用CancelThreadpoolIo,当且仅当不会通知 I/O 完成端口并且不会调用关联的 I/O 回调函数。
您必须调用
重叠(异步)I/O 操作失败(即异步 I/O 函数调用返回失败并带有错误代码 除了CancelThreadpoolIo
函数 以下场景:ERROR_IO_PENDING
)。 异步 I/O 操作立即成功返回,并且与 I/O 完成对象关联的文件句柄具有 通知模式FILE_SKIP_COMPLETION_PORT_ON_SUCCESS
。文件 句柄不会通知 I/O 完成端口和关联的 I/O 不会调用回调函数。
所以让dwErrorCode
api 返回错误代码(NOERROR
如果 win32 api 返回 TRUE)
下一个功能说你 - 将或不通知 IOCP
bool IsWillBeNotification(ULONG dwErrorCode, BOOL bSkipCompletionPortMode)
switch (dwErrorCode)
case ERROR_IO_PENDING:
return true;
case NOERROR:
return !bSkipCompletionPortMode;
default:
return false;
如果我们使用原生 API 和 NTSTATUS
bool IsWillBeNotification(NTSTATUS status, BOOL bSkipCompletionPortMode)
return (status == STATUS_PENDING) || (!NT_ERROR(status) && !bSkipCompletionPortMode);
所以:
如果STATUS_PENDING
- 将是 IOCP 数据包
如果NT_ERROR(status)
- 将没有数据包(状态在[0xC0000000,
0xFFFFFFFF]
范围内)
否则基于bSkipCompletionPortMode - 如果未设置则为
请注意,这是任何异步 io api 的一般规则。只有 NtLockFile
/LockFileEx
- 此规则的例外 - 即使 api 失败,由于 NtLockFile
实现内部的错误,也可以是 IOCP 通知
【讨论】:
以上是关于使用 io 完成端口时,WriteFile 是不是会在完成时立即发布完成数据包的主要内容,如果未能解决你的问题,请参考以下文章
使用套接字时,哪些 IO 操作会导致完成数据包发送到完成端口?