通过域套接字传输的单个数据包大小的实际限制是什么?

让我们假设为典型的服务器客户端程序创build了一个Unix域套接字。 客户端通过套接字发送一个10GB的缓冲区,同时被服务器使用。

操作系统(Linux / BSD)将10GB缓冲区分成多个数据包并发送/使用它们,还是一次发送?

如果一次不能发送10GB的域套接字缓冲区,那么单个数据包的实际大小限制是多less?

约束:

  • 该程序将在Linux 2.6.32 +和FreeBSD 9+上运行
  • 要发送的缓冲区的大小范围从3字节到10GB的最大值。

有许多因素决定了可以在Unix套接字上发送的数据包的最大大小:

  1. wmem_max套接字发送缓冲区的最大内核设置,它决定了可以使用setsockopt (SO_SNDBUF)设置的发送缓冲区的最大大小。 当前设置可以从/proc/sys/net/core/wmem_max ,可以使用sysctl net.core.wmem_max=VALUE进行设置(将设置添加到/etc/sysctl.conf以使更改在重新启动时保持不变)。 注意这个设置适用于所有套接字和套接字协议,而不仅仅是Unix套接字。

  2. 如果多个数据包发送到一个Unix套接字(使用SOCK_DATAGRAM),那么可以不阻塞地发送的最大数据量取决于套接字发送缓冲区的大小(见上) Unix上未读数据包的最大数量套接字(内核参数net.unix.max_dgram_qlen )。

  3. 最后,一个数据包(SOCK_DATAGRAM)需要连续的内存(按照可以在linux中发送的AF_UNIX数据报消息的最大大小是多少 )。 内核中有多少连续内存取决于许多因素(例如系统上的I / O负载等)。

因此,为了最大限度地提高应用程序的性能,您需要一个较大的套接字缓冲区大小(以最小化由于套接字编写系统调用而导致的用户/内核空间上下文切换)和一个大的Unix套接字队列(尽可能地分离生产者和消费者)。 但是,套接字发送缓冲区大小和队列长度的乘积不能太大以至于导致内核耗尽连续内存区域(导致写入失败)。

实际数字将取决于您的系统配置和使用情况。 您将需要通过测试来确定限制…开始用wmem_max和32位的max_dgram_qlen ,并保持加倍wmem_max直到您注意到事情开始中断。 您将需要调整max_dgram_qlen以在一定程度上平衡生产者和消费者的活动(尽管如果生产者比消费者快得多或慢得多,则队列大小不会有太大影响)。

注意你的生产者将不得不通过调用setsockopt (SO_SNDBUF)来将套接字发送缓冲区大小设置为wmem_max字节,并且将不得不将数据分割成wmem_max字节块(并且消费者将不得不重新组装它们)。

最佳猜测:实际限制将在wmem_max〜8Mb和unix_dgram_qlen〜32之间。

域套接字本身没有“数据包”。 tcp“streams”或udp“datagrams”的语义是用内核模拟的,类似于用户空间的应用程序,但是就这一点而言是这样的。 机制不像使用网络协议的网络套接字那样涉及。 你真正感兴趣的是内核将为你缓冲多少。

从程序的角度来看,这并不重要。 把套接字想象成一个管道或FIFO。 当缓冲区填满你要阻止; 如果套接字是非阻塞的,你会得到短的写入(假设流)或EAGAIN错误。 无论缓冲区的大小如何,都是如此。 但是,你应该能够使用getsockopt查询缓冲区大小,并通过setsockopt来增加它的大小,但是我怀疑你会得到接近10GB的地方。

或者,你可以看看sendfile

这里有两个想法。 一个是使用SOCK_DGRAM发送的数据包的大小,另一个是域套接字缓冲区的大小。 这取决于域套接字设置的变量。 大小可以取决于它是否是内存文件套接字。

如果你正在谈论SOCK_DGRAM,那很容易通过实验来确定。 看起来你正在谈论SOCK_STREAM的可能性更大,在这种情况下,这根本就没有关系。 SOCK_STREAM会把它排在你的外面。 只要写你喜欢的任何大小的块:越大越好。