使用 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 进行异步读写并且操作立即完成,是否会发生相同的行为?还是我必须处理?

【问题讨论】:

这里的WSARecvWriteFile 没有任何区别。两者都是异步 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 回调函数。

您必须调用CancelThreadpoolIo 函数 以下场景:

重叠(异步)I/O 操作失败(即异步 I/O 函数调用返回失败并带有错误代码 除了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 是不是会在完成时立即发布完成数据包的主要内容,如果未能解决你的问题,请参考以下文章

通过 IOCP 进行串行通信

使用套接字时,哪些 IO 操作会导致完成数据包发送到完成端口?

IOCP:内核如何决定同步或异步完成 WSASend?

fs.writeFile() 在关闭脚本时写入 [object, object] 而不是实际的对象

完成端口

窗口 HTTP IO 完成端口