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
Removing a handle from a I/O completion port and other questions about IOCP
提问by Etan
The CreateIoCompletionPort
function 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 recv
on a socket or a ReadFile
on a file with a OVERLAPPED
structure to start an asynchronous operation.
然后,我可以使用任何函数,例如recv
在套接字上或ReadFile
在具有OVERLAPPED
结构的文件上启动异步操作。
I have to check whether the function call returned synchronously although it was called with an OVERLAPPED
structure and in this case handle it directly. In the other case, when ERROR_IO_PENDING
is returned, I can use the GetQueuedCompletionStatus
function 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
recv
asynchronously but tosend
synchronously? For example when a simple echo service is implemented: Can I wait with an asynchronousrecv
for new data butsend
the response in a synchronous way so that code complexity is reduced? In my case, I wouldn'trecv
a second time anyways before the first request was processed.What happens if an asynchronous
ReadFile
has been requested, but before it completes, aWriteFile
to the same file should be processed. Will theReadFile
be 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 theReadFile
manually 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 OVERLAPPED
structure's hEvent
field: 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 OVERLAPPED
structure. In fact, you may even extend OVERLAPPED
to 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
/WriteFile
returns TRUE
. You must explicitly call SetFileCompletionNotificationModesto tell Windows to not enqueue a completion packet when TRUE
and ERROR_SUCCESS
are returned.
这是默认行为,即使ReadFile
/WriteFile
返回TRUE
。您必须显式调用SetFileCompletionNotificationModes以告诉 Windows在返回TRUE
和时不要将完成数据包排入队列ERROR_SUCCESS
。
is it possible for example to
recv
asynchronously but tosend
synchronously?
例如,是否可以
recv
异步但send
同步?
Not by using recv
and send
; you need to use functions that accept OVERLAPPED
structures, such as WSARecv
, WSASend
, or alternatively ReadFile
and 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.
不是通过使用recv
and send
; 你需要使用接受功能OVERLAPPED
结构,如WSARecv
,WSASend
,或者ReadFile
和WriteFile
。如果您的代码旨在处理多种类型的 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 OVERLAPPED
structures 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 NtSetInformationFile
with the FileReplaceCompletionInformation
enumerator value for FileInformationClass
and a pointer to a FILE_COMPLETION_INFORMATION
structure for the FileInformation
parameter. In this structure, set the Port
member 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 Port
to a valid HANDLE
to another completion port to associate the file with that one instead.
调用NtSetInformationFile
与FileReplaceCompletionInformation
用于枚举值FileInformationClass
和一个指向FILE_COMPLETION_INFORMATION
的结构FileInformation
参数。在此结构中,将Port
成员设置为NULL
(或nullptr
,在 C++ 中)以将文件与其当前附加到的端口分离(我猜如果它未附加到任何端口,则不会发生任何事情),或设置Port
为HANDLE
对另一个有效完成端口将文件与该文件相关联。
回答by valdo
First some important corrections.
首先是一些重要的更正。
In case the overlapped I/O operation completes immediately (ReadFile
or 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:
现在,关于您的问题:
- 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!
- 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 GetQueuedCompletionStatus
in a loop) should perform the per-I/O needed cleanup.
更正确的问题应该是文件/套接字应该如何正确关闭。答案是:只要合上你的把手。所有未完成的 I/O 操作(在此句柄上发出)将很快返回并带有错误代码(中止)。然后,在您的完成例程(GetQueuedCompletionStatus
在循环中调用的那个)应该执行每个 I/O 需要的清理。
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
).You should use
WSASend
andWSARecv
(notrecv
andsend
) for overlapped I/O. Nevertheless, even of the socket was opened with flagWSA_FLAG_OVERLAPPED
- you are allowedto call the I/O functions without specifying theOVERLAPPED
structure. In such a case those functions work synchronously. So that you may decide on synchronous/asynchronous modes for every function call.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.
正如我已经说过的,在同步和异步情况下,所有 I/O 完成都到达 IOCP。它没有到达 IOCP 的唯一情况是当 I/O 同步完成并出现错误时。无论如何,如果您想要统一处理 - 在这种情况下,您可以将人工完成数据发布到 IOCP(使用
PostQueuedCompletionStatus
)。对于重叠的 I/O,您应该使用
WSASend
andWSARecv
(而不是recv
andsend
)。尽管如此,即使是使用标志打开的套接字WSA_FLAG_OVERLAPPED
- 您也可以在不指定OVERLAPPED
结构的情况下调用 I/O 函数。在这种情况下,这些功能会同步工作。这样您就可以为每个函数调用决定同步/异步模式。混合重叠的读/写请求没有问题。这里唯一的微妙之处是如果您尝试从当前正在写入的文件位置读取数据会发生什么。结果可能取决于一些微妙的事情,例如硬件完成 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 (CreateIoCompletionPort
does 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_OVERLAPPED
in CreateFile
to 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_OVERLAPPED
inCreateFile
才能打开异步 I/O。然而,在本机层,同步 I/O 确实是一个附加的、方便的事情,内核会为您跟踪文件位置并等待 I/O 完成后再返回。