![如何知道 TCP 缓冲区实际使用了多少内存?](https://linux22.com/image/126331/%E5%A6%82%E4%BD%95%E7%9F%A5%E9%81%93%20TCP%20%E7%BC%93%E5%86%B2%E5%8C%BA%E5%AE%9E%E9%99%85%E4%BD%BF%E7%94%A8%E4%BA%86%E5%A4%9A%E5%B0%91%E5%86%85%E5%AD%98%EF%BC%9F.png)
我有一台前端机器,有大约 1k 个持久的、非常低带宽的 TCP 连接。它的内存有点有限,所以我试图找出几百 MB 的去向。 TCP 缓冲区可能是罪魁祸首之一,但我无法解决这些问题:
- 内存报告在哪里?它是
buff/cache
中项目的一部分top
,还是流程RES
度量的一部分? - 如果我想在每个进程级别上减少它,如何确保我的减少达到预期的效果?
- 即使流量最小,缓冲区是否仍继续占用一些内存,或者缓冲区大小是否动态增长,而缓冲区大小仅仅是允许的最大大小?
我意识到一个可能的答案是“相信内核会为你做这件事”,但我想排除 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
,以及它的神秘Buffers
和Cached
物品。我查看了多个来源,但找不到任何声称它占 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-Q
和Send-Q
,当前缓冲区使用情况r
和t
,其解释在这篇优秀的文章,但尚不清楚它们与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_max
是net.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