如何判断 TCP 缓冲区实际使用了多少内存?

Mik*_*her 16 tcp

我有一台前端机器,它有大约 1k 个持久的、非常低带宽的 TCP 连接。它有点内存受限,所以我试图弄清楚几百 MB 的去向。TCP 缓冲区是一个可能的罪魁祸首,但我无法回答这些问题:

  1. 内存报告在哪里?它是 中buff/cache项目的top一部分,还是流程RES指标的一部分?
  2. 如果我想在每个流程级别上减少它,我如何确保我的减少达到预期的效果?
  3. 即使流量最小,缓冲区是否会继续占用一些内存,或者它们是否动态增长,缓冲区大小只是允许的最大大小?

我意识到一个可能的答案是“相信内核会为你做这件事”,但我想排除 TCP 缓冲区作为内存压力的来源。

调查:问题 1

该页面写道,“‘缓冲区’内存是 Linux 用于缓冲网络和磁盘连接的内存。” 这意味着,他们不是的部分RES指标在top

找到实际的内存使用情况,/proc/net/sockstat是最有希望的:

sockets: used 3640
TCP: inuse 48 orphan 49 tw 63 alloc 2620 mem 248
UDP: inuse 6 mem 10
UDPLITE: inuse 0
RAW: inuse 0
FRAG: inuse 0 memory 0
Run Code Online (Sandbox Code Playgroud)

是我能找到的最好的解释,但mem没有在那里解决。它在此处解决,但 248*4k ~= 1MB,或大约系统范围最大值的 1/1000,对于具有数百个持久连接和持续 .2-.3Mbit/sec 网络流量的服务器来说,这似乎是一个低得离谱的数字.

当然,系统内存限制本身是:

$ grep . /proc/sys/net/ipv4/tcp*mem
/proc/sys/net/ipv4/tcp_mem:140631   187510  281262
/proc/sys/net/ipv4/tcp_rmem:4096    87380   6291456
/proc/sys/net/ipv4/tcp_wmem:4096    16384   4194304
Run Code Online (Sandbox Code Playgroud)

tcp_mem的第三个参数是系统范围内专用于 TCP 缓冲区的最大 4k 页数;如果缓冲区大小的总和超过此值,内核将开始丢弃数据包。对于非外来工作负载,无需调整此值。

接下来是/proc/meminfo,和它的神秘BuffersCached物品。我查看了几个来源,但找不到任何声称它占 TCP 缓冲区的来源。

...
MemAvailable:    8298852 kB
Buffers:          192440 kB
Cached:          2094680 kB
SwapCached:        34560 kB
...
Run Code Online (Sandbox Code Playgroud)

调查:问题 2-3

为了在进程级别检查 TCP 缓冲区大小,我们有很多选项,但它们似乎都没有提供实际分配的内存而不是当前队列大小或最大值。

ss -m --info

State       Recv-Q Send-Q
ESTAB       0      0
... <snip> ....
skmem:(r0,rb1062000,t0,tb2626560,f0,w0,o0,bl0)  ...<snip> rcv_space:43690
Run Code Online (Sandbox Code Playgroud)

所以我们有

  • Recv-QSend-Q,当前缓冲区使用情况
  • rt,这在这篇优秀的帖子中进行了解释,但目前还不清楚它们与Recv-QSend-Q
  • 名为 的东西rb,看起来很像某种最大缓冲区大小,但我找不到任何文档
  • rcv_space该页面声称这不是实际的缓冲区大小;为此你需要打电话getsockopt

这个答案表明lsof,但大小/关闭似乎报告了与以下相同的缓冲区使用情况ss

COMMAND     PID   TID                USER   FD      TYPE             DEVICE SIZE/OFF       NODE NAME
sslocal    4032                   michael   82u     IPv4            1733921      0t0        TCP localhost:socks->localhost:59594 (ESTABLISHED)
Run Code Online (Sandbox Code Playgroud)

然后这些答案表明 lsof 无法返回实际的缓冲区大小。它确实提供了一个应该可以解决问题的内核模块,但它似乎只适用于缓冲区大小已被固定的套接字setsockopt;如果不是,则不包括 SO_SNDBUF 和 SO_RCVBUF。

Mik*_*her 13

/proc/net/sockstat,特别是该mem领域,是要看的地方。该值在内核页面中报告并直接对应于/proc/sys/net/ipv4/tcp_mem.

在单个套接字级别,内存仅在内核空间分配,直到用户空间代码读取它,此时内核内存被释放(请参阅此处)。sk_buff->truesize是缓冲的数据量以及套接字结构本身的总和(请参阅此处此处讨论了针对内存对齐进行校正的补丁)

我怀疑该mem字段/proc/net/sockstat只是通过对sk_buff->truesize所有套接字求和来计算的,但我对内核源代码不够熟悉,不知道在哪里查找。

顺便确认一下,来自netdata监控系统的这个特性请求中也包含了很多很好的讨论和相关链接,并且支持了/proc/net/sockstat.

这篇关于“out of socket memory”错误的帖子包含对不同内存问题的一些更一般的讨论。


Tyl*_*ers 7

这是一个非常复杂的问题,可能需要深入研究内核源代码才能找到答案。

似乎缓冲区并未包含在进程的 RES 统计信息中。请参阅这篇文章(如果您还没有)。据作者介绍:

设备驱动程序为设备分配一个内存区域来对传入的数据包执行 DMA

在部分进一步向下“调节:插座接收队列存储器”好像net.core.wmem_maxnet.core.rmem_max为最大缓冲区的大小。同样,不确定如何查看实际使用了多少内存。

显然,在网络堆栈中存在文档差的问题,并且显然存在大量复杂性。这里是

此外,我对缓冲处理方式的了解越多,除了作为缓冲区分配了多少内存之外,vanilla 内核似乎不支持查看任何其他内容。

内核中有关 DMA 的一点文档也可能对您有用,或者至少让您了解可以从这里开始,但现在我认为提供的内核模块是您可能获得的最接近的.