如果 I/O 完成端口数据包可能以不同的顺序出列,为啥 I/O 完成端口数据包会按 FIFO 顺序排队?
Posted
技术标签:
【中文标题】如果 I/O 完成端口数据包可能以不同的顺序出列,为啥 I/O 完成端口数据包会按 FIFO 顺序排队?【英文标题】:Why are I/O Completion Port Packets Queued in FIFO order if they may be dequeued in a different order?如果 I/O 完成端口数据包可能以不同的顺序出列,为什么 I/O 完成端口数据包会按 FIFO 顺序排队? 【发布时间】:2015-01-15 02:17:14 【问题描述】:Microsoft 的 I/O Completion Ports 文档指出:
请注意,虽然 [completion] 数据包按 FIFO 顺序排队,但它们可能会以不同的顺序出队。
据我了解,线程通过调用GetQueuedCompletionStatus从完成端口获取完成包。如果系统不保证数据包将按 FIFO 顺序检索,为什么系统会按 FIFO 顺序将数据包排队到完成端口?
【问题讨论】:
我不明白什么是“先进先出-出”,甚至当第一个出的不是第一个入的时候…… @Mehrdad 很高兴知道我不是唯一一个感到困惑的人。 maybe 意思是简单的情况下,使用单线程,包可能是FIFO的,但是不同的线程条件可能会改变顺序。 IE。 “在 FIFO 中排队”应该是“按插入顺序排队”。但出队机制因不同原因而有所不同 FIFO 仍然有意义,即使它不是严格的 FIFO。它告诉你缓冲区会有吞吐量。不像 LIFO 堆栈,其中可能有数据包“卡在底部”。 【参考方案1】:您引用的语句旨在让您意识到,如果需要在单个套接字上的 I/O 完成之间进行排序,则需要进行自己的排序。如果您在单个套接字上发出多个 WSARecv 调用,您可能需要这个。当它们完成时,完成将按 FIFO 顺序进入 IOCP 队列,这将是发出 WSARecv 调用的顺序。
如果您继续阅读该文档,您将看到这篇文章:
在 I/O 完成端口上阻止其执行的线程是 以后进先出 (LIFO) 顺序发布,下一次完成 数据包从 I/O 完成端口的 FIFO 队列中拉出 线。这意味着,当完成数据包被释放到 线程,系统释放关联的最后一个(最近的)线程 使用该端口,将最旧的完成信息传递给它 I/O 完成。
这表明完成以 FIFO 顺序从 IOCP 中删除。第一个注释的原因是,如果您有多个线程在 IOCP 上等待,那么线程调度问题可能意味着您的代码处理完成的顺序与从 IOCP 中检索它们的顺序不同。
假设您有 2 个线程服务于一个 IOCP 和一个带有 3 个 WSARecv 待处理的 TCP 套接字。足够的数据来自网络以完成所有三个待处理的 WSARecv,因此您最终在 IOCP 中完成了三个;我们将它们称为 A、B 和 C。这些是发出 WSARecv 调用的顺序,因此应处理缓冲区 A、B 和 C 中的数据以保持 TCP 流的健全性。
您的第一个 IOCP 线程将完成 A。第二个线程将完成 B。取决于您的硬件(内核数量等)和 OS 调度程序,线程 1 或线程 2 可能接下来运行或者两者可以同时运行。这可能会导致您在上述情况下出现问题。
我个人通过在编写可以在单个套接字上发出多个 WSARecvs 的服务器时向每个缓冲区添加一个序列号来解决这个问题。序列号递增,插入缓冲区并在同一个锁内发出 WSARecv,因此整个操作是原子的。当完成发生时,我确保只有一个线程处理给定套接字的缓冲区(请参阅here),或者我使用“顺序缓冲区集合”可以确保以正确的顺序处理缓冲区(请参阅here) .
另请注意,为确保正确性,您需要锁定在给定套接字上发出 WSARecv(和 WSASend)调用(请参阅here)
【讨论】:
感谢您分享您的见解,Len。您的解释和示例使我相信 IOCP 实际上确实按照我在看到 Microsoft 的“[完成] 数据包以 FIFO 顺序排队 [并且] 可能以不同的顺序出列之前所期望的方式工作。 "评论。如果我理解正确,那么如果文档说明“完成数据包始终按 FIFO 顺序排队和出列到完成端口/从完成端口出列”,那么文档将是准确的。标准线程行为(调度等)会影响线程从完成端口出列的顺序” 这是我对此的理解,也是我的单元测试在使用仅由单个线程提供服务的 IOCP 时显示的内容。【参考方案2】:-
首先,尽可能使缓冲区足够大以接收所有数据。
不要为一个套接字多次发布 WSARecv 接收。
连接后或读取数据后,处理这些日期,最后发布 WSARecv。
sum: 任何时候一个socket只有一个接收缓冲区。
我只为一个 iocp 句柄完成了服务器端、客户端、文件读取、写入、TCP 和 UDP 的 IOCP 服务器。
【讨论】:
几年前我做了一个测试。将接收到的缓冲区大小设置为 TCP 滑块窗口大小的两倍(TCPv4 最大为 64k),以获得最佳性能。以上是关于如果 I/O 完成端口数据包可能以不同的顺序出列,为啥 I/O 完成端口数据包会按 FIFO 顺序排队?的主要内容,如果未能解决你的问题,请参考以下文章
I/O 完成端口,如何释放每个套接字上下文和每个 I/O 上下文?