windows 从 I/O 完成端口删除句柄和其他有关 IOCP 的问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6573218/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 17:17:37  来源:igfitidea点击:

Removing a handle from a I/O completion port and other questions about IOCP

cwindowsfilesocketsiocp

提问by Etan

The CreateIoCompletionPortfunction allows the creation of a new I/O completion port and the registration of file handles to an existing I/O completion port.

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

Then, I can use any function, like a recvon a socket or a ReadFileon a file with a OVERLAPPEDstructure to start an asynchronous operation.

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

I have to check whether the function call returned synchronously although it was called with an OVERLAPPEDstructure and in this case handle it directly. In the other case, when ERROR_IO_PENDINGis returned, I can use the GetQueuedCompletionStatusfunction to be notified when the operation completes.

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

The question which arise are:

出现的问题是:

  • How can I remove a handle from the I/O completion port? For example, when I add sockets to the IOCP, how can I remove closed ones? Should I just re-register another socket with the same completion key?

  • Also, is there a way to make the calls ALWAYS go over the I/O completion port and don't return synchronously?

  • And finally, is it possible for example to recvasynchronously but to sendsynchronously? For example when a simple echo service is implemented: Can I wait with an asynchronous recvfor new data but sendthe response in a synchronous way so that code complexity is reduced? In my case, I wouldn't recva second time anyways before the first request was processed.

  • What happens if an asynchronous ReadFilehas been requested, but before it completes, a WriteFileto the same file should be processed. Will the ReadFilebe cancelled with an error message and I have to restart the read process as soon as the write is complete? Or do I have to cancel the ReadFilemanually before writing? This question arises in combination with a communication device; so, the write and read should not do problems if happening concurrently.

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

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

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

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

回答by Aaron Klotz

How can I remove a handle from the I/O completion port?

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

In my experience you can't disassociate a handle from a completion port. However, you may disable completion port notification by setting the low-order bit of your OVERLAPPEDstructure's hEventfield: See the documentation for GetQueuedCompletionStatus.

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

For example, when I add sockets to the IOCP, how can I remove closed ones? Should I just re-register another socket with the same completion key?

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

It is not necessary to explicitly disassociate a handle from an I/O completion port; closing the handle is sufficient. You may associate multiple handles with the same completion key; the best way to figure out which request is associated with the I/O completion is by using the OVERLAPPEDstructure. In fact, you may even extend OVERLAPPEDto store additional data.

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

Also, is there a way to make the calls ALWAYS go over the I/O completion port and don't return synchronously?

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

That is the default behavior, even when ReadFile/WriteFilereturns TRUE. You must explicitly call SetFileCompletionNotificationModesto tell Windows to not enqueue a completion packet when TRUEand ERROR_SUCCESSare returned.

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

is it possible for example to recvasynchronously but to sendsynchronously?

例如,是否可以recv异步但send同步?

Not by using recvand send; you need to use functions that accept OVERLAPPEDstructures, such as WSARecv, WSASend, or alternatively ReadFileand WriteFile. It might be more handy to use the latter if your code is meant to work multiple types of I/O handles, such as both sockets and named pipes. Those functions provide a synchronous mode, so if you use those them you can mix asynchronous and synchronous calls.

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

What happens if an asynchronous ReadFile has been requested, but before it completes, a WriteFile to the same file should be processed?

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

There is no implicit cancellation. As long as you're using separate OVERLAPPEDstructures for each read/write to a full-duplex device, I see no reason why you can't do concurrent I/O operations.

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

回答by Keno

As I've already pointed out there, the commonly held belief that it is impossible to remove handles from completion ports is wrong, probably caused by the abscence of any hint whatsoever on how to do this from nearly all documentation I could find. Actually, it's pretty easy:

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

Call NtSetInformationFilewith the FileReplaceCompletionInformationenumerator value for FileInformationClassand a pointer to a FILE_COMPLETION_INFORMATIONstructure for the FileInformationparameter. In this structure, set the Portmember to NULL(or nullptr, in C++) to disassociate the file from the port it's currently attached to (I guess if it isn't attached to any port, nothing would happen), or set Portto a valid HANDLEto another completion port to associate the file with that one instead.

调用NtSetInformationFileFileReplaceCompletionInformation用于枚举值FileInformationClass和一个指向FILE_COMPLETION_INFORMATION的结构FileInformation参数。在此结构中,将Port成员设置为NULL(或nullptr,在 C++ 中)以将文件与其当前附加到的端口分离(我猜如果它未附加到任何端口,则不会发生任何事情),或设置PortHANDLE对另一个有效完成端口将文件与该文件相关联。

回答by valdo

First some important corrections.

首先是一些重要的更正。

In case the overlapped I/O operation completes immediately (ReadFileor similar I/O function returns success) - the I/O completion is alreadyscheduled to the IOCP.

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

Also, according to your questions I think you confuse between the file/socket handles, and the specific I/O operations issued on them.

另外,根据您的问题,我认为您混淆了文件/套接字句柄以及对它们发出的特定 I/O 操作。

Now, regarding your questions:

现在,关于您的问题:

  1. AFAIK there is no conventional way to removea file/socket handle from the IOCP (usually you just don't have to do this). You talk about removing closedhandles from the IOCP, which is absolutely incorrect. You can't remove a closed handle, because it does not reference a valid kernel object anymore!
  1. AFAIK 没有从 IOCP 中删除文件/套接字句柄的传统方法(通常您不必这样做)。您谈论从 IOCP 中删除关闭的句柄,这是绝对不正确的。您不能删除关闭的句柄,因为它不再引用有效的内核对象!

A more correct question should be how the file/socket should be properly closed. The answer is: just close your handle. All the outstanding I/O operations (issued on this handle) will return soon with an error code (abortion). Then, in your completion routine (the one that calls GetQueuedCompletionStatusin a loop) should perform the per-I/O needed cleanup.

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

  1. As I've already said, all the I/O completion arrives at IOCP in both synchronous and asynchronous cases. The only situation where it does not arrive at IOCP is when an I/O completes synchronously with an error. Anyway, if you want a unified processing - in such a case you may post an artificial completion data to IOCP (use PostQueuedCompletionStatus).

  2. You should use WSASendand WSARecv(not recvand send) for overlapped I/O. Nevertheless, even of the socket was opened with flag WSA_FLAG_OVERLAPPED- you are allowedto call the I/O functions without specifying the OVERLAPPEDstructure. In such a case those functions work synchronously. So that you may decide on synchronous/asynchronous modes for every function call.

  3. There is no problem to mix overlapped read/write requests. The only delicate point here is what happens if you try to read the data from the file position where you're currently writing to. The result may depend on subtle things, such as order of completion of I/Os by the hardware, some PC timing parameters and etc. Such a situation should be avoided.

  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 时序参数等。应该避免这种情况。

回答by wj32

How can I remove a handle from the I/O completion port? For example, when I add sockets to the IOCP, how can I remove closed ones? Should I just re-register another socket with the same completion key?

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

You've got it the wrong way around. You set the I/O completion port to be used by a file object - when the file object is deleted, you have nothing to worry about. The reason you're getting confused is because of the way Win32 exposes the underlying native API functionality (CreateIoCompletionPortdoes two very different things in one function).

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

Also, is there a way to make the calls ALWAYS go over the I/O completion port and don't return synchronously?

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

This is how it's always been. Only starting with Windows Vista can you customize how the completion notifications are handled.

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

What happens if an asynchronous ReadFile has been requested, but before it completes, a WriteFile to the same file should be processed. Will the ReadFile be cancelled with an error message and I have to restart the read process as soon as the write is complete?

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

I/O operations in Windows are asynchronous inherently, and requests are always queued. You may not think this is so because you have to specify FILE_FLAG_OVERLAPPEDin CreateFileto turn on asynchronous I/O. However, at the native layer, synchronous I/O is really an add-on, convenience thing where the kernel keeps track of the file position for you and waits for the I/O to complete before returning.

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