在抓取数据包之后会发生什么?

12

我一直在阅读关于NIC捕获数据包后发生的事情,越看越迷惑。

首先,我了解到,传统上,在NIC捕获数据包后,它会被复制到内核空间中的一块内存中,然后再被复制到用户空间供应用程序处理。但是我也读到了关于DMA的内容,其中NIC直接将数据包复制到内存中,绕过CPU。那么,NIC->内核内存->用户空间内存的流程仍然有效吗?此外,大多数NIC(例如Myricom)是否使用DMA来提高数据包捕获率?

其次,RSS(Receive Side Scaling)在Windows和Linux系统中的工作方式是否相似?我只能在MSDN文章中找到RSS的详细说明,其中介绍了RSS(和MSI-X)在Windows Server 2008上的工作原理。但是RSS和MSI-X的相同概念应该也适用于Linux系统,对吗?

谢谢。

问候, Rayne


1
谷歌搜索零拷贝网络...目前很少有高速设备能够容忍内核和用户之间的双重复制。我不是网络专家,但我相信DMA被积极使用。 - Sam Post
所以现在,一旦网卡捕获到一个数据包,它就会直接被复制到用户内存中? - Rayne
3
是的,网卡将使用DMA直接将数据传输到映射到您进程(用户态)地址空间的物理内存中。 - vladr
1
为什么你要问这个问题?你想要达到什么目标? - Dénes Tarján
@vladr,正如您所提到的,NIC通过DMA直接将数据包复制到用户空间进程的内存中,并且仅使用一份副本...但我认为DMA仅用于在内核内存中传输数据而无需CPU干预,因此数据的复制不是由CPU完成的,而CPU可能会在DMA传输时工作。 - akp
显示剩余2条评论
2个回答

18
这个过程的具体实现方式大多数取决于驱动程序作者和硬件,但根据我所查看或编写的驱动程序和我所用的硬件来看,通常是以下方式进行的:
  1. 在驱动程序初始化时,它会分配一些缓冲区并将其提供给网络接口卡(NIC)。
  2. 当NIC接收到数据包时,它会从缓冲区列表中取出下一个地址,直接将数据DMA进入其中,并通过中断通知驱动程序。
  3. 驱动程序获得中断后,可以将缓冲区交给内核,或者它将分配一个新的内核缓冲区并复制数据。 "零拷贝网络"是前者,显然需要操作系统的支持。(下面有更多关于此的内容)
  4. 驱动程序需要分配新的缓冲区(在零拷贝情况下),或者它将重新使用该缓冲区。无论哪种情况,缓冲区都将被返回给NIC以备未来的数据包使用。
在内核中实现无拷贝网络并不那么困难。但是,一直到用户空间的无拷贝实现要难得多。用户空间获取数据,但网络数据包由头部和数据组成。至少,真正的零拷贝一直到用户空间需要NIC的支持,以便它可以将数据包DMA到单独的头部/数据缓冲区中。当内核将数据包路由到目的地并验证校验和(对于TCP,在NIC支持时在硬件中进行,在否则在软件中进行;请注意,如果内核必须自己计算校验和,则可能会复制数据:查看数据会导致缓存未命中,并且将其复制到其他位置也可以使用经过调整的代码免费完成)后,头部将被回收利用。
即使假设所有条件都满足,当系统接收到数据时,数据实际上并不在您的用户缓冲区中。在应用程序请求数据之前,内核不知道它将最终到达何处。考虑像Apache这样的多进程守护程序的情况。有许多子进程,所有子进程都在监听同一个套接字。您还可以建立连接,fork(),两个进程都能够recv()传入的数据。

互联网上的TCP数据包通常为1460字节有效负载(MTU为1500 = 20字节IP头+20字节TCP头+1460字节数据)。1460不是2的幂,也不会与任何系统的页面大小匹配。这给数据流的重组带来了问题。请记住,TCP是面向流的。发送者写入之间没有区别,并且在接收方等待的两个1000字节写入将完全消耗在一个2000字节读取中。

更进一步,考虑用户缓冲区。这些由应用程序分配。为了从始至终都能使用零拷贝设计,缓冲区需要按页面对齐,并且不与任何其他内容共享该内存页面。在recv()时间,内核理论上可以重新映射旧页面到包含数据的页面并将其“翻转”到位,但由于上述重组问题,连续的数据包将位于不同的页面上,这就很复杂了。内核可以限制它返回的数据量为每个数据包的有效负载,但这意味着会有很多额外的系统调用、页面映射和可能降低整体吞吐量。

我只是浅尝辄止地谈到了这个主题。在21世纪初,我曾在几家公司工作,试图将零拷贝的概念扩展到用户空间。我们甚至在用户空间实现了一个TCP堆栈,并绕过内核为使用该堆栈的应用程序,但这带来了自己一套问题,并且从未达到生产质量。这是一个非常难解决的问题。


-1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接