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

如何知道 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

是我能找到的最好的解释,但mem没有在那里解决。已解决这里,但 248*4k ~= 1MB,或大约是系统范围最大值的 1/1000,对于具有数百个持久连接和持续 0.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

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

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

...
MemAvailable:    8298852 kB
Buffers:          192440 kB
Cached:          2094680 kB
SwapCached:        34560 kB
...

调查:问题 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

所以我们有

  • Recv-QSend-Q,当前缓冲区使用情况
  • rt,其解释在这篇优秀的文章,但尚不清楚它们与Recv-Q和有何不同Send-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)

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

答案1

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

在单个套接字级别,仅在用户空间代码读取它之前才在内核空间中分配内存,此时内核内存将被释放(请参阅这里)。sk_buff->truesize是缓冲数据量以及套接字结构本身的总和(请参阅这里,并讨论了纠正内存对齐的补丁这里

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

通过确认的方式,此功能请求来自 netdata 监控系统的内容包括很多很好的讨论和相关链接,并且它支持了这种解释/proc/net/sockstat

这个帖子关于“套接字内存不足”错误包含一些对不同内存问题的更一般性讨论。

答案2

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

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

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

在“调整:套接字接收队列内存”部分中,看起来 和net.core.wmem_maxnet.core.rmem_max最大缓冲区大小。再次,不确定如何查看实际使用了多少内存。

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

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

内核中有关 DMA 的一些文档可能对您也有用,或者至少让您了解可以从这里转到哪里,但目前我认为提供的内核模块是您可以获得的最接近的。

答案3

如前所述,mem 字段是与您的问题相关的字段。

# cat /proc/net/sockstat
sockets: used 2512
TCP: inuse 2301 orphan 48 tw 3187 alloc 2304 mem 981
UDP: inuse 12 mem 2
UDPLITE: inuse 0
RAW: inuse 2
FRAG: inuse 0 memory 0

# echo $(( 981 * 4096  / 2**20 ))MB
3MB

相关内容