【问题标题】:Count of memory copies in *nix systems between packet at NIC and user application?*nix 系统中 NIC 的数据包和用户应用程序之间的内存副本计数?
【发布时间】:2010-04-21 13:08:35
【问题描述】:

这只是一个与我一直想知道的一些高性能计算有关的一般性问题。某个低延迟消息传递供应商在其支持文档中谈到使用原始套接字将数据直接从网络设备传输到用户应用程序,并且这样做它谈到了比它更进一步减少消息传递延迟(在其他公认的深思熟虑的设计决策)。

因此,我的问题是针对那些在 Unix 或类 Unix 系统上了解网络堆栈的人。使用这种方法,他们可能实现多少差异?随意回答记忆副本、获救的鲸鱼数量或威尔士大小的区域;)

据我了解,他们的消息传递是基于 UDP 的,因此建立 TCP 连接等没有问题。关于此主题的任何其他兴趣点将不胜感激!

祝你好运,

迈克

【问题讨论】:

  • 这将是非常系统特定的。您应该更具体地提及您感兴趣的系统。
  • 公平地说,我最感兴趣的是这与 Linux 的关系。我在桌面上运行了 OSX,但可以远程到 Linux 服务器来玩测试程序。

标签: networking tcp udp low-latency


【解决方案1】:

有一些图片http://vger.kernel.org/~davem/tcp_output.html 用 Google 搜索 tcp_transmit_skb() 这是 tcp 数据路径的关键部分。他的网站上有一些更有趣的东西http://vger.kernel.org/~davem/

user - tcp transmit 部分数据路径中有 1 份 从用户到带有skb_copy_to_page 的 skb(当通过 tcp_sendmsg() 发送时)) 和 0 副本do_tcp_sendpages(由 tcp_sendpage() 调用)。需要复制以保留数据备份,以防未交付的段。内核中的 skb 缓冲区可以被克隆,但它们的数据将保留在第一个(原始)skb 中。 Sendpage 可以从其他内核部分获取一个页面并保留它以供备份(我认为有类似 COW 的东西)

调用路径(手动来自 lxr)。发送tcp_push_one/__tcp_push_pending_frames

tcp_sendmsg() <-  sock_sendmsg <- sock_readv_writev <- sock_writev <- do_readv_writev

tcp_sendpage() <- file_send_actor <- do_sendfile 

接收tcp_recv_skb()

tcp_recvmsg() <-  sock_recvmsg <- sock_readv_writev <- sock_readv <- do_readv_writev

tcp_read_sock() <- ... spliceread for new kernels.. smth sendfile for older

receive 中,可以有 1 个副本 从内核到用户 skb_copy_datagram_iovec(从 tcp_recvmsg 调用)。对于 tcp_read_sock() 可以有副本。它将调用sk_read_actor 回调函数。如果它对应于文件或内存,它可能(也可能不会)从 DMA 区域复制数据。如果是其他网络,它有一个接收数据包的 skb,并且可以就地重用其数据。

对于 udp - 接收 = 1 份副本 - 从 udp_recvmsg 调用的 skb_copy_datagram_iovec。传输 = 1 个副本 -- udp_sendmsg -> ip_append_data -> getfrag(似乎是 ip_generic_getfrag 与用户的 1 个副本,但可能是一个没有页面复制的 smth sendpage/splicelike。)

一般来说,从用户空间发送/接收到用户空间时必须至少有 1 个副本,而在使用零拷贝(惊喜!)和内核空间源/目标缓冲区的数据时必须至少有 0 个副本。在不移动数据包的情况下添加所有标头,启用 DMA 的(所有现代)网卡将从启用 DMA 的地址空间中的任何位置获取数据。旧卡需要 PIO,所以会多一份,从内核空间到 PCI/ISA/smthelse I/O 寄存器/内存。

UPD:在从 NIC(但这取决于 nic,我也检查了 8139)到 tcp 堆栈的路径中,还有一个副本:从 rx_ring 到 skb,接收相同:从 skb 到tx 缓冲区+1copy。 ip 和 tcp header 必须填写,但是 skb 是否包含它们或放置它们?

【讨论】:

  • “Linux 网络性能分析 - 数据包接收”(thnx to hackingnasdaq.blogspot.com/2010/01/… - tcp_low_latency sysctl 的神话)
  • hackingnasdaq.blogspot.com - 这个博客很有趣。有很多关于低延迟linux网络的帖子
  • “Linux TCP 的潜在性能瓶颈” 是 WWu 和 MCrawford 撰写的另一篇关于 linux 网络数据包路径的非常好的(并且更长)论文跨度>
  • 哇 - 很棒的答案。太糟糕了,你不能将它们修改超过一个点......但我可以看到这可能会导致什么!不过,也修改了您的其他答案。干杯osgx!斯帕西巴
  • @Michael_73,我希望这将成为我论文的一部分 :)
【解决方案2】:

为了减少高性能的延迟,您应该拒绝使用内核驱动程序。使用用户空间驱动程序可以实现最小的延迟(MX 可以做到,Infinband 也可以)。

Linux 网络内部结构“A Map of the Networking Code in Linux Kernel 2.4.20”有一个相当不错(但有些过时)的概述。 TCP/UDP数据路径有一些方案。

使用原始套接字将使 tcp 数据包的路径更短一些(感谢您的想法)。内核中的 TCP 代码不会增加其延迟。但是用户必须自己处理所有的 tcp 协议。有一些机会针对某些特定情况对其进行优化。集群代码不需要像默认 TCP/UDP 堆栈那样处理长距离链接或慢速链接。

我也对这个主题很感兴趣。

【讨论】:

  • 2.4.20 (NAPI) 的网络内部结构仍然适用于 2.6。但是有新的 sendfile(sendpage)/splice 接口用于消除副本。
  • 这似乎是一个非常有趣的话题。从 Java 工程师的角度来看,我也对它感兴趣——通过将其交给本地高性能网络实现,网络性能(吞吐量/延迟/无 GC)可以达到什么程度。在阅读了 Java 部分被视为一种网络语言之后,我最近读到一篇谴责 JVM 的网络复制效率低下的论文,我感到有些惊讶,尽管这至少部分与 JNI 有关。也许 JVM 的一个未来方向可能是对一些目标操作系统的网络代码做一些特别的事情。
  • 顺便说一句,如果您尝试通过 PF_PACKET 套接字或 BPF/pcap/libnet 变体接收 TCP 数据包,Stevens 的书“Unix 网络编程”有一种巧妙的方法可以阻止操作系统发送 RST。
  • @Michael_73,有趣...你能给出更精确的链接到这个功能吗?我现在也不知道,操作系统如何过滤传入的数据包以区分发送到 RAW 套接字的数据包和其他包。
  • 在 Google 图书上很幸运 - 他们没有删除该特定页面。请参阅第 794 页底部的单个缩进段落books.google.co.uk/…。否则谷歌“解决这个问题的一种方法是使用属于附加子网的源 IP 地址发送 TCP 段”;)
猜你喜欢
  • 1970-01-01
  • 2016-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-13
  • 1970-01-01
  • 2010-12-10
  • 2011-12-10
相关资源
最近更新 更多