当进程在 Linux 虚拟机上调用“connect”时,我看到错误消息“无可用缓冲区空间”。我无法找出原因 - 希望有人能帮忙!
我已检查以下内容:
(1)文件句柄:
cat /proc/sys/fs/file-nr
4672 0 810707
我将其读为(已分配、未使用、可用),因此看起来没问题。
(2)套接字或TCP内存:
cat /proc/sys/net/ipv4/tcp_mem
191889 255854 383778
cat /proc/net/sockstat
sockets: used 579
TCP: inuse 169 orphan 0 tw 245 alloc 187 mem 5
UDP: inuse 31 mem 4
UDPLITE: inuse 0
RAW: inuse 0
FRAG: inuse 0 memory 0
读到这里,发现总共只使用了 579 个插座,页面总数远低于最大值。
Google 上有很多随机 TCP 调整 - 我希望得到的答案是 (1) 我所用资源不足,(2) 如何确定当前值,以及 (3) 如何调整上限。我发现的大多数页面都缺少除 (3) 之外的所有内容!
** 更新 #1 **
根据 Flup 的建议,当它发生时我做了一个系统跟踪(使用 ping):
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(1025), sin_addr=inet_addr("10.140.0.65")}, 16) = -1 ENOBUFS (No buffer space available)
** 更新 #2 **
我不太了解 Linux 内核源代码,但是我仔细研究了一下,在 connect() 路径中我唯一能看到 ENOBUFS 的地方是这里: http://lxr.free-electrons.com/source/net/ipv4/af_inet.c?v=3.11#L353
这看起来像是在内核中分配东西,但是有kmem_cache_alloc
…… security_sk_alloc
?
答案1
在 3.6 之前的内核中,当 net.ipv4.route.max_size 或 net.ipv6.route.max_size因此,限额已耗尽。
从内核 3.6 开始,路由缓存已被删除,并且 net.ipv4.route.max_size 不再影响 dst 条目的数量。因此,一般来说,这不再可能。
但是,使用 IPSec 时,您仍然会像我一样遇到此错误。在创建一定数量的 IPSec 隧道后,我无法 ping 远程主机:
# ping 10.100.0.1
connect: No buffer space available
iputils 中的 ping 创建测试文件描述符,并在其上使用 connect() 来绑定目标 IP。发生这种情况时,内核会为给定的 AF 创建目标缓存条目,而 xfrm4 目标缓存条目限制在我的情况下已经耗尽。此限制由 sysctl 设置控制:
xfrm4_gc_thresh - INTEGER
The threshold at which we will start garbage collecting for IPv4
destination cache entries. At twice this value the system will
refuse new allocations.
我在使用内核 3.10.59 时遇到过这个问题,其中默认限制非常低 - 1024。从内核 3.10.83 开始,此限制增加到 32768,并且将是更难击中。
因此,我发布了:
# sysctl net.ipv4.xfrm4_gc_thresh=32768
它帮我实现了这个目标。
对于我使用 IPSec 的情况,内核中的近似路径为:
ip4_datagram_connect() -> ip_route_connect() -> ip_route_output_flow() ->
xfrm_lookup() -> xfrm_resolve_and_create_bundle() ->
... -> xfrm_alloc_dst() -> dst_alloc() with xfrm4_dst_ops, where gc is set.
答案2
好吧,我不知道问题到底是什么,但我会尝试找出解决问题的正确方向。
当或失败ENOBUFS
时,将返回代码。我在与套接字相关的源代码中找不到任何其他出现的代码。sk_alloc()
dst_alloc()
ENOBUFS
另外,我找不到从SYSCALL_DEFINE3(connect)
到的任何路径sk_alloc()
,并且我认为在出现错误的调用期间不应该分配套接字connect()
,因此我认为不太可能sk_alloc()
导致问题。
很dst_alloc()
可能用于在期间检查路线connect()
,我找不到它的确切路径,它一定在里面的某个地方:SYSCALL_DEFINE3(connect)
->.connect()
->ip4_datagram_connect()
->ip_route_connect()
在相应的 SLAB 缓存中分配dst_alloc()
一个条目,如果缓存已满,它实际上可能会失败。如果发生这种情况,实际上应该清除旧条目,但可能有些情况下它仍然会返回错误。
所以我认为你可以朝这个方向发展。dst 缓存大小可能通过 更改/proc/sys/net/ipv4/route/max_size
。首先,检查设置(或 中的任何其他设置sys.net.ipv4.route
)是否被“Google 上显示的随机 TCP 调整”更改。