Linux中AF_UNIX数据报消息的最大大小是多少?

20

目前我遇到了一个严格限制为130688字节的硬性限制。如果我尝试发送任何一条更大的消息,我会收到一个ENOBUFS错误。

我已经检查了net.core.rmem_defaultnet.core.wmem_defaultnet.core.rmem_maxnet.core.wmem_maxnet.unix.max_dgram_qlen系统控制选项,并将它们全部增加了,但是它们没有任何效果,因为这些选项处理的是总缓冲区大小而不是消息大小。

我还设置了SO_SNDBUFSO_RCVBUF套接字选项,但这与上述问题相同。默认的套接字缓冲区大小是基于默认套接字选项设置的。

我查看了ENOBUFS在套接字栈中返回的内核源代码,但我不清楚它来自哪里。似乎唯一返回这个错误的地方与无法分配内存有关。

最大大小是否实际上是130688?如果不是,可以在不重新编译内核的情况下更改吗?


2
那是一个巨大的数据报。在我看来,一旦你拥有了这么大的数据报,你可能可以使用TCP。 - Jon Trauntvein
1
是的,那并没有帮助。正如我在帖子中所述,无论您的wmem设置如何,它都不会让您发送超过130688的消息。我已将它们设置为超过32MB,并尝试了许多低于该值的组合。 - Jaime
1
只是补充一下。有一个误解,即发送缓冲区和接收缓冲区仅用于单个消息。该缓冲区是所有消息的总内核缓冲区。 wmem 和 qlen sysctl 选项实际上会影响何时以及何种方式发送阻塞。当发送缓冲区填满(假设没有人接收),当缓冲区中的总字节数将超出缓冲区大小或总计数将超出 qlen 时,发送将被阻止。 - Jaime
我更好地理解了你的观点(和问题)。删除了混淆的评论并点赞;如果时间允许,我会进行探索,因为我也对答案感兴趣。 - JB.
那是128kB减去一些开销(384字节)。对于我来说,这听起来非常合理,作为消息的最大大小。如果您超过外部接口,则限制将更低。 - Donal Fellows
2
我同意这可能是硬极限。只是想找到一些证据和可能的原因。 - Jaime
1个回答

20

AF_UNIX SOCK_DATAGRAM/SOCK_SEQPACKET 数据报需要连续内存。连续的物理内存很难找到,分配失败,在内核日志中记录类似以下内容:

udgc: page allocation failure. order:7, mode:0x44d0
[...snip...]
DMA: 185*4kB 69*8kB 34*16kB 27*32kB 11*64kB 1*128kB 1*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 3788kB
Normal: 13*4kB 6*8kB 100*16kB 62*32kB 24*64kB 10*128kB 0*256kB 1*512kB 0*1024kB 0*2048kB 0*4096kB = 7012kB
[...snip...]

unix_dgram_sendmsg()调用sock_alloc_send_skb()lxr1,该函数使用data_len=0和header_len=数据报大小调用sock_alloc_send_pskb()lxr2sock_alloc_send_pskb()从“正常”的skbuff缓冲区空间分配header_len,并从散布/聚集页面中分配data_lenlxr3。因此,看起来当前的Linux上AF_UNIX套接字不支持散布/聚集。


2
干得好。这基本上是我在跟踪中发现的,但您提供了实际原因。我不知道为什么数据报会有这个限制,而流则没有? - Jaime
2
SOCK_STREAM套接字不保留消息边界。 - ninjalj
请参阅 https://dev59.com/bWEh5IYBdhLWcg3w7nSW 的详细回答。 - Jonathan Ben-Avraham
SOCK_STREAM套接字肯定会保留消息边界。只需将每个消息放入单独的流中:创建一个新套接字,连接它,写入消息,关闭发送方向,接收回复并断开连接。然后为下一个消息重复此过程。也就是说,连接支持帧因为它们具有明确定义的“流开始”和“流结束”信号;它们只是不支持流内的帧。就像SOCK_DGRAM套接字不支持数据报内的帧一样。 - Kaz
5
@Kaz,所以他们不这样做,你必须假装。 - lvella

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