在内核模式下不可能消除发送完成路径。原因是网卡忙于从内存中读取字节,直到它最终发出发送完成。如果您在重新使用数据包之前没有等待发送完成,那么网卡将没有机会读取完整的数据包。您最终会发送损坏的数据。
但是,您是对的,当使用库存 NDISPROT 样本发送大量数据时效率非常低。问题是 NDISPROT 的用户模式示例应用程序将数据同步写入内核模式。这意味着您的线程开始写入(发送数据包),然后阻塞直到写入(发送数据包)完成。(该示例效率低下,因为 NDISPROT 示例的目的是说明如何在内核模式下与 NDIS 进行互操作,而不是说明用户与内核通信的复杂技术。)
您可以通过使用多种技术中的一种同时发布多条数据来大大加快速度:
使用多线程。做你现在正在做的同样的事情,除了在多个线程上并行执行。这很容易设置,但扩展性不是很好(要扩展至 10 倍流量,您需要 10 倍线程,然后您开始在缓存问题上受到伤害)。另外,如果你的数据集必须按顺序发送,你需要一堆复杂的同步来确保线程按顺序发出请求。
对 WriteFile 和 OVERLAPPED 数据结构使用异步调用。这需要您对用户模式应用程序进行一些改造。(幸运的是,您不需要接触内核驱动程序,因为它已经支持这一点)。使用 OVERLAPPED 写入,您可以从单个线程发出多个同时写入,然后在其中任何(或全部)完成时收到通知。如果您对重叠设计足够小心,您应该能够轻松填充 100Mbps 网络链接。
更明确地说,这是您目前拥有的:
Your app NDISPROT driver Network card The network
---------------------------------------------------------------------------------
WriteFile
. \-------> NdisProtWrite
. \-------> NdisSendPackets
. |
. (copy packet payload
. from system RAM to
. network card's buffer)
. |
. |---------------> Start sending
. NdisProtSendComplete <---------| .
WriteFile <----/ | .
returns |<--------------- Finish sending
如您所见,在网卡将数据包有效负载从 RAM 复制到 NIC 硬件的整个过程中,您的用户模式应用程序一直停留在 WriteFile 中。相反,如果您对内核模式使用异步写入,您将得到以下结果:
Your app NDISPROT driver Network card The network
---------------------------------------------------------------------------------
WriteFile
. \-------> NdisProtWrite
. | \-------> NdisSendPackets
WriteFile <------/ |
returns (copy packet payload
from system RAM to
network card's buffer)
|
|---------------> Start sending
NdisProtSendComplete <---------| .
Async write <--/ | .
completes |<--------------- Finish sending
在此设置中,WriteFile 返回更快,因此您有机会在 NIC 仍在读取第一个数据包时排队另一个数据包(或 10 个)。您可以使用任何常用的 OVERLAPPED 技术来确定写入(发送数据包)何时完成,并且您可以重用数据缓冲区。
要开始使用异步 I/O,请从本文档开始。(哎呀,看起来他们的图表从我很棒的 ASCII-art 旋转了 90°...)。