8

CreateIoCompletionPort函数允许创建新的 I/O 完成端口并将文件句柄注册到现有的 I/O 完成端口。

然后,我可以使用任何函数,例如recv套接字上的 a 或ReadFile具有结构的文件上的 aOVERLAPPED来启动异步操作。

我必须检查函数调用是否同步返回,尽管它是用OVERLAPPED结构调用的,在这种情况下直接处理它。在另一种情况下,当ERROR_IO_PENDING返回时,我可以使用该GetQueuedCompletionStatus函数在操作完成时得到通知。

出现的问题是:

  • 如何从 I/O 完成端口移除句柄?例如,当我向 IOCP 添加套接字时,如何删除已关闭的套接字?我应该用相同的完成键重新注册另一个套接字吗?

  • 另外,有没有办法让调用总是通过 I/O 完成端口并且不同步返回?

  • 最后,是否有可能例如recv异步但send同步?例如,当实现一个简单的回显服务时:我可以异步等待recv新数据,但send以同步方式响应,从而降低代码复杂性吗?recv就我而言,在处理第一个请求之前,我不会再做第二次了。

  • 如果ReadFile已请求异步,但在完成之前,WriteFile应处理同一文件的 a,会发生什么情况。是否会因ReadFile错误消息而取消,并且我必须在写入完成后立即重新启动读取过程?还是我必须ReadFile在写之前手动取消?这个问题与通信设备结合出现;因此,如果同时发生,写入和读取不应该有问题。

4

4 回答 4

11

如何从 I/O 完成端口移除句柄?

根据我的经验,您无法将句柄与完成端口分离。OVERLAPPED但是,您可以通过设置结构字段的低位来禁用完成端口通知hEvent:请参阅GetQueuedCompletionStatus的文档。

例如,当我向 IOCP 添加套接字时,如何删除已关闭的套接字?我应该用相同的完成键重新注册另一个套接字吗?

没有必要显式地解除句柄与 I/O 完成端口的关联;关闭手柄就足够了。您可以将多个句柄与同一个完成键关联;找出哪个请求与 I/O 完成相关联的最佳方法是使用该OVERLAPPED结构。事实上,您甚至可以扩展OVERLAPPED以存储额外的数据。

另外,有没有办法让调用总是通过 I/O 完成端口并且不同步返回?

这是默认行为,即使ReadFile/WriteFile返回时也是如此TRUE。您必须显式调用SetFileCompletionNotificationModes来告诉 Windows 在返回时不要将完成数据包排入TRUE队列ERROR_SUCCESS

例如可以recv异步但send同步吗?

不是通过使用recvand send; 您需要使用接受OVERLAPPED结构的函数,例如WSARecv, WSASend, 或者ReadFileand WriteFile。如果您的代码旨在处理多种类型的 I/O 句柄,例如套接字和命名管道,则使用后者可能更方便。这些函数提供了同步模式,因此如果您使用它们,您可以混合使用异步和同步调用。

如果已请求异步 ReadFile,但在完成之前,应处理同一文件的 WriteFile,会发生什么情况?

没有隐式取消。只要您对OVERLAPPED全双工设备的每次读/写都使用单独的结构,我认为您没有理由不能进行并发 I/O 操作。

于 2011-07-04T15:21:22.080 回答
9

正如我已经指出的那样,人们普遍认为不可能从完成端口中删除句柄是错误的,这可能是由于我能找到的几乎所有文档中都没有任何关于如何执行此操作的提示。实际上,这很容易:

NtSetInformationFile使用FileReplaceCompletionInformation枚举值 forFileInformationClass和指向参数FILE_COMPLETION_INFORMATION结构的指针调用FileInformation。在这个结构中,将Port成员设置为NULL(或nullptr,在 C++ 中)以取消文件与其当前附加到的端口的关联(我猜如果它没有附加到任何端口,什么都不会发生),或者设置为对另一个Port有效HANDLE完成端口将文件与该文件相关联。

于 2016-02-16T17:13:07.217 回答
3

首先是一些重要的更正。

如果重叠的 I/O 操作立即完成(ReadFile或类似的 I/O 函数返回成功) - I/O 完成已经安排到 IOCP。

另外,根据您的问题,我认为您在文件/套接字句柄和对它们发出的特定 I/O 操作之间感到困惑。

现在,关于您的问题:

  1. AFAIK 没有传统的方法可以从 IOCP 中删除文件/套接字句柄(通常您不必这样做)。您谈论从 IOCP 中删除已关闭的句柄,这是绝对不正确的。您不能删除已关闭的句柄,因为它不再引用有效的内核对象!

一个更正确的问题应该是如何正确关闭文件/套接字。答案是:只要合上你的把手。所有未完成的 I/O 操作(在此句柄上发出)将很快返回错误代码(中止)。然后,在您的完成例程中(在循环中调用的例程GetQueuedCompletionStatus)应该执行每个 I/O 所需的清理。

  1. 正如我已经说过的,在同步和异步情况下,所有 I/O 完成都会到达 IOCP。它没有到达 IOCP 的唯一情况是 I/O 同步完成并出现错误。无论如何,如果您想要统一处理 - 在这种情况下,您可以将人工完成数据发布到 IOCP(使用PostQueuedCompletionStatus)。

  2. 对于重叠的 I/O,您应该使用WSASendand WSARecv(而不是recvand )。send尽管如此,即使是使用标志打开的套接字WSA_FLAG_OVERLAPPED- 您也可以OVERLAPPED在不指定结构的情况下调用 I/O 函数。在这种情况下,这些功能会同步工作。这样您就可以为每个函数调用决定同步/异步模式。

  3. 混合重叠的读/写请求没有问题。这里唯一微妙的一点是,如果您尝试从当前正在写入的文件位置读取数据,会发生什么。结果可能取决于一些细微的事情,例如硬件完成 I/O 的顺序、一些 PC 时序参数等。应该避免这种情况。

于 2011-07-04T15:31:49.683 回答
0

如何从 I/O 完成端口移除句柄?例如,当我向 IOCP 添加套接字时,如何删除已关闭的套接字?我应该用相同的完成键重新注册另一个套接字吗?

你搞错了。您将 I/O 完成端口设置为由文件对象使用 - 当文件对象被删除时,您无需担心。您感到困惑的原因是因为 Win32 公开底层本机 API 功能的方式(CreateIoCompletionPort在一个函数中做两件非常不同的事情)。

另外,有没有办法让调用总是通过 I/O 完成端口并且不同步返回?

一直都是这样。只有从 Windows Vista 开始,您才能自定义完成通知的处理方式。

如果已请求异步 ReadFile,但在完成之前,应处理同一文件的 WriteFile,会发生什么情况。ReadFile 是否会因错误消息而被取消,并且我必须在写入完成后立即重新启动读取过程?

Windows 中的 I/O 操作本质上是异步的,并且请求总是排队。您可能不这么认为,因为您必须指定FILE_FLAG_OVERLAPPEDinCreateFile才能打开异步 I/O。但是,在本机层,同步 I/O 确实是一个附加的、方便的东西,内核会为您跟踪文件位置并等待 I/O 完成后再返回。

于 2011-07-05T13:28:31.760 回答