网络收发过程中,缓冲区位置在哪里?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络收发过程中,缓冲区位置在哪里?相关的知识,希望对你有一定的参考价值。

参考技术A 在 关于 Linux 网络,你必须要知道这些 中,我曾介绍过 Linux 网络的收发流程。这个流程涉及到了多个队列和缓冲区,包括:

不过相应的,就会有两个问题。

首先,这些缓冲区的位置在哪儿?是在网卡硬件中,还是在内存中?这个问题其实仔细想一下,就很容易明白——这些缓冲区都处于内核管理的内存中。

其中,环形缓冲区,由于需要 DMA 与网卡交互,理应属于网卡设备驱动的范围。

sk_buff 缓冲区,是一个维护网络帧结构的双向链表,链表中的每一个元素都是一个网络帧(Packet)。虽然 TCP/IP 协议栈分了好几层,但上下不同层之间的传递,实际上只需要操作这个数据结构中的指针,而无需进行数据复制。

套接字缓冲区,则允许应用程序,给每个套接字配置不同大小的接收或发送缓冲区。应用程序发送数据,实际上就是将数据写入缓冲区;而接收数据,其实就是从缓冲区中读取。至于缓冲区中数据的进一步处理,则由传输层的 TCP 或 UDP 协议来完成。

其次,这些缓冲区,跟前面内存部分讲到的 Buffer 和 Cache 有什么关联吗?

这个问题其实也不难回答。我在内存模块曾提到过,内存中提到的 Buffer ,都跟块设备直接相关;而其他的都是 Cache。

实际上,sk_buff、套接字缓冲、连接跟踪等,都通过 slab 分配器来管理。你可以直接通过 /proc/slabinfo,来查看它们占用的内存大小。

第二个问题,内核协议栈的运行,是按照一个内核线程的方式吗?在内核中,又是如何执行网络协议栈的呢?

说到网络收发,在中断处理文章中我曾讲过,其中的软中断处理,就有专门的内核线程 ksoftirqd。每个 CPU 都会绑定一个 ksoftirqd 内核线程,比如, 2 个 CPU 时,就会有 ksoftirqd/0 和 ksoftirqd/1 这两个内核线程。

不过要注意,并非所有网络功能,都在软中断内核线程中处理。内核中还有很多其他机制(比如硬中断、kworker、slab 等),这些机制一起协同工作,才能保证整个网络协议栈的正常运行。

我们知道,无论 TCP 还是 UDP,端口号都只占 16 位,也就说其最大值也只有 65535。那是不是说,如果使用 TCP 协议,在单台机器、单个 IP 地址时,并发连接数最大也只有 65535 呢?

对于这个问题,首先你要知道,Linux 协议栈,通过五元组来标志一个连接(即协议,源 IP、源端口、目的 IP、目的端口)。

明白了这一点,这个问题其实就有了思路。我们应该分客户端和服务器端,这两种场景来分析。

对客户端来说,每次发起 TCP 连接请求时,都需要分配一个空闲的本地端口,去连接远端的服务器。由于这个本地端口是独占的,所以客户端最多只能发起 65535 个连接。

对服务器端来说,其通常监听在固定端口上(比如 80 端口),等待客户端的连接。根据五元组结构,我们知道,客户端的 IP 和端口都是可变的。如果不考虑 IP 地址分类以及资源限制,服务器端的理论最大连接数,可以达到 2 的 48 次方(IP 为 32 位,端口号为 16 位),远大于 65535。

所以,综合来看,客户端最大支持 65535 个连接,而服务器端可支持的连接数是海量的。当然,由于 Linux 协议栈本身的性能,以及各种物理和软件的资源限制等,这么大的连接数,还是远远达不到的(实际上,C10M 就已经很难了)。

Linux 多播中排队/缓冲延迟的可能位置在哪里?

【中文标题】Linux 多播中排队/缓冲延迟的可能位置在哪里?【英文标题】:Where are possible locations of queueing/buffering delays in Linux multicast? 【发布时间】:2011-01-18 02:44:43 【问题描述】:

我们在 LAN 上的许多 Linux 服务器上大量使用多播消息传递。我们看到很多延误。我们基本上会发送大量的小包裹。我们更关心延迟而不是吞吐量。这些机器都是现代的多核机器(如果算上超线程,至少有 4 个,通常是 8 个,16 个)机器,负载总是 2.0 或更低,通常负载小于 1.0。网络硬件的容量也不足 50%。

我们看到的延迟看起来像排队延迟:数据包的延迟会迅速增加,直到看起来像堵塞,然后恢复正常。

消息结构基本上是这样的:在“发送线程”中,从队列中拉出消息,添加时间戳(使用gettimeofday()),然后调用send()。接收程序接收消息,为接收时间加上时间戳,并将其推送到队列中。在一个单独的线程中,处理队列,分析发送和接收时间戳之间的差异。 (请注意,我们的内部队列不是问题的一部分,因为时间戳是在我们的内部队列之外添加的。)

我们真的不知道从哪里开始寻找这个问题的答案。我们不熟悉 Linux 内部结构。我们的怀疑是内核在发送端或接收端(或两者)对数据包进行排队或缓冲。但我们不知道如何追踪它。

不管怎样,我们使用的是 CentOS 4.x(RHEL 内核 2.6.9)。

【问题讨论】:

从做wireshark跟踪开始,这样你就会看到排队是因为网络设备,还是丢包(即使是简单的交换机也疯狂排队,通常是丢包的主要来源) 我们绝对不会丢包,因为我们对所有内部消息进行排序并检查丢包。运行像 Wireshark 这样的东西是侵入性的,不适合生产环境。我们无法在测试环境中复制这些延迟。因此,我们希望从逻辑上理解 Linux 内核在多播发送和接收方面的行为。 另外,我还要补充一点,在某些情况下,发送者和接收者在同一台机器上。在这种情况下,我们仍然会看到这些延迟效应。这样就解决了问题的网络硬件部分。 【参考方案1】:

这是一个很好的问题。在 CentOS 上,像大多数 *nix 风格一样,每个多播套接字都有一个 UDP 接收/发送缓冲区。此缓冲区的大小由 sysctl.conf 控制,您可以通过调用 /sbin/sysctl -a 查看缓冲区的大小

以下项目显示了我的默认和最大 udp 接收大小(以字节为单位)。这些数字越大,如果您的应用程序消耗数据的速度太慢,网络/内核可能会引入更多的缓冲和延迟。如果您对数据丢失建立了良好的容忍度,则可以使这些缓冲区非常小,并且您将看不到上述延迟的增加和恢复。权衡是缓冲区溢出时数据丢失 - 您可能已经看到了。

[~]$ /sbin/sysctl -a |内存 net.core.rmem_default = 16777216 net.core.wmem_default = 16777216 net.core.rmem_max = 16777216 net.core.wmem_max = 16777216

在大多数情况下,您需要将 default = 设置为您的最大值,除非您在创建套接字时控制它。

您可以做的最后一件事(取决于您的内核版本)是查看您的进程或至少整个框的 PID 的 UDP 统计信息。

cat /proc/net/snmp | grep -i udp Udp:InDatagrams NoPorts InErrors OutDatagrams udp:81658157063 145 616548928 3896986

cat /proc/PID/net/snmp | grep -i udp Udp:InDatagrams NoPorts InErrors OutDatagrams udp:81658157063 145 616548928 3896986

如果从我的帖子中不清楚,延迟是由于您的应用程序没有足够快地消耗数据并迫使内核缓冲上述结构中的流量。网络、内核,甚至你的网卡环形缓冲区都可以在延迟方面发挥作用,但所有这些项目通常只会增加几毫秒。

让我知道您的想法,我可以为您提供更多信息,让您了解如何在应用中查看以提高性能。

【讨论】:

所以说在发送端没有内核缓冲是否公平?在接收端,没有内核缓冲,除非数据消耗得不够快?不幸的是,我没有每个 PID 的 UDP 统计信息(我不确定我的 2.6.9-78.ELlargesmp 内核是否不够新,或者根本没有为此配置)。无论如何,我绝对对任何有关从我们的应用程序中获得更多性能的信息感兴趣。另请注意,我们实际上关心的是“只有几毫秒”——这实际上是一个实时应用程序。谢谢! 您的总结几乎 100% 符合我对 linux 中的多播堆栈的理解。唯一的区别是,无论您在另一端消费数据的速度有多快,“发送”端都可能发生排队。发送端排队是网速不足、网卡质量差、一般性能问题(负载、内存等)的结果。发送端排队不是很常见。是的,这一切都在毫秒或更多的范围内。 还有一点,如果您的网卡在发送端使用 intel e1000 驱动程序,由于驱动程序处理硬件中断的方式,您会看到 0.1 毫秒的延迟突发。【参考方案2】:

数据包可以在发送和接收端内核、NIC 和网络基础设施中排队。您会发现大量可以测试和调整的项目。

对于 NIC,您通常可以找到中断合并参数 - NIC 在通知内核或在等待批处理数据包时发送到线路之前将等待多长时间。

对于 Linux,您有发送和接收“缓冲区”,它们越大,您就越有可能在批处理操作中处理数据包时遇到更高的延迟。

对于架构和 Linux 版本,您必须了解上下文切换的成本以及是否启用了锁或抢先调度。考虑最小化运行的应用程序数量,使用进程关联性将进程锁定到特定内核。

不要忘记计时,您使用的 Linux 内核版本在 gettimeofday() 时钟 (2-4ms) 上的精度非常差,而且调用成本非常高。考虑使用替代方案,例如从核心 TSC 或外部 HPET 设备读取数据。

英特尔的图表: alt text http://www.theinquirer.net/IMG/142/96142/latency-580x358.png?1272514422

【讨论】:

【参考方案3】:

如果您决定需要在生产环境中捕获数据包,则可能值得考虑使用交换机上的监控端口并使用非生产机器捕获数据包。这还可以让您在传输路径上的多个点捕获数据包并比较您所看到的。

【讨论】:

另外考虑使用 TS-Associates TipOff 等专用硬件设备的众多咨询公司或监控专家之一:ts-associates.com/view/tipoff

以上是关于网络收发过程中,缓冲区位置在哪里?的主要内容,如果未能解决你的问题,请参考以下文章

网络基础--对套接字描述符的本质理解

select 设置发送超时发送注意事项

socket收发消息原理剖析

Linux 多播中排队/缓冲延迟的可能位置在哪里?

怎样将数据放入一个缓冲区stm32

做网络爬虫过程过程中遇到的问题