Home > Linux, 工具介绍, 网络编程, 调优 > 网络栈内存不足引发进程挂起问题

网络栈内存不足引发进程挂起问题

February 26th, 2013

原创文章,转载请注明: 转载自系统技术非业余研究

本文链接地址: 网络栈内存不足引发进程挂起问题

我们知道TCP socket有发送缓冲区和接收缓冲区,这二个缓冲区都可以透过setsockopt设置SO_SNDBUF,SO_RCVBUF来修改,但是这些值设多大呢?这些值和协议栈的内存控制相关的值什么关系呢?
我们来解释下:

$ sysctl net|grep mem
net.core.wmem_max = 131071
net.core.rmem_max = 131071
net.core.wmem_default = 124928
net.core.rmem_default = 124928
net.core.optmem_max = 20480
net.ipv4.igmp_max_memberships = 20
net.ipv4.tcp_mem = 4631520 6175360 9263040
net.ipv4.tcp_wmem = 4096 16384 4194304
net.ipv4.tcp_rmem = 4096 87380 4194304
net.ipv4.udp_mem = 4631520 6175360 9263040
net.ipv4.udp_rmem_min = 4096
net.ipv4.udp_wmem_min = 4096

下面的图很好的解释了上面的问题:

socket_mem

这里要记住的是:TCP协议栈内存是不可交换物理内存,用一字节少一字节。
也正是由于这一点,操作系统出厂的时候上面的默认的内存设置都不算太大。对于一个不是网络密集型的服务器问题不大,但是对于如承担C1M链接的服务器来讲,问题就来了。我们在实践中会发现tcp服务经常超时,有时候超过100ms. 那么这个问题如何定位呢?

我们知道当协议栈缺少内存的时候会调用sk_stream_wait_memory等待其他进程释放出内存,所以这个函数的等待时间就是我们的进程被阻塞的时间。
下面我们来验证下:

$ cat /usr/share/doc/systemtap-1.6/examples/network/sk_stream_wait_memory.stp
#!/usr/bin/stap
# Simple probe to detect when a process is waiting for more socket send
# buffer memory. Usually means the process is doing writes larger than the
# socket send buffer size or there is a slow receiver at the other side.
# Increasing the socket's send buffer size might help decrease application
# latencies, but it might also make it worse, so buyer beware.
#
# Typical output: timestamp in microseconds: procname(pid) event
#
# 1218230114875167: python(17631) blocked on full send buffer
# 1218230114876196: python(17631) recovered from full send buffer
# 1218230114876271: python(17631) blocked on full send buffer
# 1218230114876479: python(17631) recovered from full send buffer

probe kernel.function("sk_stream_wait_memory")
{
        printf("%u: %s(%d) blocked on full send buffer\n",
                gettimeofday_us(), execname(), pid())
}

probe kernel.function("sk_stream_wait_memory").return
{
        printf("%u: %s(%d) recovered from full send buffer\n",
                gettimeofday_us(), execname(), pid())
}
$ sudo stap sk_stream_wait_memory.stp  
1218230114875167: python(17631) blocked on full send buffer
1218230114876196: python(17631) recovered from full send buffer
1218230114876271: python(17631) blocked on full send buffer
1218230114876479: python(17631) recovered from full send buffer

如果我们观察到了进程由于缺少内存被阻塞,那么是时候调整协议栈的内存限制了。

小结:网络很复杂,需要定量分析!

祝玩得开心。

Post Footer automatically generated by wp-posturl plugin for wordpress.

  1. hongmeng
    February 26th, 2013 at 13:36 | #1

    感谢分享

  2. February 26th, 2013 at 14:02 | #2

    那些参数都需要调吗? 能否举例说明?

    Yu Feng Reply:

    就是图上的4个参数。

  3. February 26th, 2013 at 14:42 | #3

    不错!感谢。

  4. February 27th, 2013 at 17:10 | #4

    这个对于1G带宽的网卡而言 默认的mem应该不会有影响的吧?

    Yu Feng Reply:

    和网卡的带宽没有直接关系,而是和socket的多少以及如何使用内存很大关系。

  5. liansm
    February 27th, 2013 at 17:11 | #5

    问个问题server端accept后生成的socket是不是也可以设置SO_SNDBUF和SO_REVBUF?
    不知道这个缓冲区是每个socket都是独立的还是共享的?

  6. evan
    March 5th, 2013 at 10:27 | #6

    net.core.wmem_max = 131071,net.ipv4.tcp_wmem = 4096 16384 4194304,这两个配置有关联的吗?例如这里max:4194304 覆盖 max:131071?

    Yu Feng Reply:

    这些简单的去翻下内核 都可以解释。

  7. hekeqin
    March 24th, 2013 at 18:25 | #7

    你好,如果线上的服务真出现这种情况,如何监控呢?系统日志里有记录吗

    Yu Feng Reply:

    系统没有日志呀。要不为什么要这个脚本?

  8. chenzhanyiczy
    April 11th, 2013 at 11:56 | #8

    ”我们知道当协议栈缺少内存的时候会调用sk_stream_wait_memory等待其他进程释放出内存,所以这个函数的等待时间就是我们的进程被阻塞的时间。“

    霸爷这句话说的模糊了。
    sk_stream_wait_memory 并不是只是在协议栈缺少内存的时候才会调用。在单个socket没有发送缓冲区空间的时候(或者说发送缓冲区满的时候),也会最终调用该函数。

    也就说说,来到sk_stream_wait_memory()调用的有以下三个情景:
    1、发送缓冲区满了,又有新的要发送数据(此时send会阻塞在该函数中)
    2、不能分配skb
    3、不能分配page
    其中1受到wmem_max限制;
    2和3受到tcp_mem的限制
    也,1和2、3没有必然的联系。
    举个例子:在一个socket中,即使1没有满足条件(即发送缓冲区没有满,后续的send可以填充缓冲区),那2、3同样可能在tcp连接相当多的时候发生。

    不管上面哪个情况发生,sk_stream_wait_memory作用都只是在等待时间内检查发送缓冲区是否有可用的空间。
    所以调整这些参数要分情况。

    我理解对吗?

    Yu Feng Reply:

    确实你的表述很清楚,多谢!

Comments are closed.