/proc/sys/net/ipv4/tcp_mem
确定TCP栈应该如何反映内存使用,每个值的单位都是内存页(通常是4KB)。第一个值是内存使用的下限;第二个值是内存压力模式开始对缓冲区使用应用压力的上限;第三个值是内存使用的上限。在这个层次上可以将报文丢弃,从而减少对内存的使用。对于较大的BDP可以增大这些值(注意,其单位是内存页而不是字节)。
tcp_mem(3个INTEGER变量):low, pressure, high
- low:当TCP使用了低于该值的内存页面数时,TCP不会考虑释放内存。(理想情况下,这个值应与指定给 tcp_wmem 的第 2 个值相匹配 - 这第 2 个值表明,最大页面大小乘以最大并发请求数除以页大小 (131072 * 300 / 4096)。 )
- pressure:当TCP使用了超过该值的内存页面数量时,TCP试图稳定其内存使用,进入pressure模式,当内存消耗低于low值时则退出pressure状态。(理想情况下这个值应该是 TCP 可以使用的总缓冲区大小的最大值 (204800 * 300 / 4096)。 )
- high:允许所有tcp sockets用于排队缓冲数据报的页面量。(如果超过这个值,TCP 连接将被拒绝,这就是为什么不要令其过于保守 (512000 * 300 / 4096) 的原因了。 在这种情况下,提供的价值很大,它能处理很多连接,是所预期的 2.5 倍;或者使现有连接能够传输 2.5 倍的数据。 我的网络里为192000 300000 732000)
1 | static void tcp_init_mem(void) |
sndbuf是根据什么定的?业务
在实际的过程中发送数据和接收数据量比较大,可以设置socket缓冲区,而避免了send(),recv()不断的循环收发
为了达到最大网络吞吐,socket send buffer size(SO_SNDBUF)不应该小于带宽和延迟的乘积。 之前我遇到2个性能问题,都和SO_SNDBUF设置得太小有关。 但是,写程序的时候可能并不知道把SO_SNDBUF设多大合适,而且SO_SNDBUF也不宜设得太大,浪费内存啊。 于是,有OS提供了动态调整缓冲大小的功能,这样应用程序就不用再对SO_SNDBUF调优了。
(接受缓冲SO_RCVBUF也是类似的问题,不应该小于带宽和延迟的乘积)。
(1)当设置的值val > 最大值sysctl_wmem_max,则设置为最大值的2倍:2sysctl_wmem_max;
(2)当设置的值的两倍val2 <最小值,则设置成最小值:SOCK_MIN_SNDBUF;
(3)当设置的值val < 最大值sysctl_wmem_max,且 val2> SOCK_MIN_SNDBUF, 则设置成2val。
存放接收缓冲区最大值的位置:/proc/sys/net/core/rmem_max
存放发送缓冲区最大值的位置:/proc/sys/net/core/wmem_max
Linux从2.4开始支持接收缓冲和发送缓冲的动态调整。
如果指定了tcp_wmem,则net.core.wmem_default被tcp_wmem的覆盖。send Buffer在tcp_wmem的最小值和最大值之间自动调整。如果调用setsockopt()设置了socket选项SO_SNDBUF,将关闭发送端缓冲的自动调节机制,tcp_wmem将被忽略,SO_SNDBUF的最大值由net.core.wmem_max限制。
初步结论:
内核发送缓存区是存在上限的,如果并发连接太多,而且socket的SO_SNDBUF比较大,会导致前面的连接就把内核发送缓存区用完了,导致后面的连接一直发不出数据,此时调小socket的SO_SNDBUF,就可以解决该问题。比如内核发送缓存区总大小为60M,设置socket的SO_SNDBUF为1M,则只能让60M/1M=60个连接同时使用,如果将socket的SO_SNDBUF改为200K,则可以60M/200K=307个连接并发使用。
可修改方案:根据并发连接数动态调整SO_SNDBUF(并发大的时候调小),或者不设置SO_SNDBUF,或者调大/proc/sys/net/ipv4/tcp_mem参数
- 采用sar -n DEV 1能更好的观察收发的速度,sar -n SOCK 1可以查看当前使用的socket数。
- 除了使用netstat查看tcp连接的收发缓冲区大小,还可以使用ss命令,另外/proc/net/tcp可以看到更详细的tcp连接信息,比如拥塞窗口,重传次数等。
- 可以在代码里使用ioctl(fd, SIOCOUTQ, &pending);查看socket发送缓冲区的大小,ioctl(fd, SIOCINQ, &pending);为接收缓冲区,头文件sys/ioctl.h、linux/sockios.h。
限制了接收新 TCP 连接侦听队列的大小。对于一个经常处理新连接的高负载 web服务环境来说,默认的 128 太小了。大多数环境这个值建议增加到 1024 或者更多。 服务进程会自己限制侦听队列的大小(例如 sendmail(8) 或者 Apache),常常在它们的配置文件中有设置队列大小的选项。大的侦听队列对防止拒绝服务 DoS 攻击也会有所帮助。
我们可以通过,echo 1000 >/proc/sys/net/core/somaxconn来修改这个参数。
cat /proc/net/sockstat
sockets: used 294
TCP: inuse 38 orphan 0 tw 0 alloc 48 mem 4
UDP: inuse 4 mem 0
UDPLITE: inuse 0
RAW: inuse 0
FRAG: inuse 0 memory 0
sockets: used:已使用的所有协议套接字总量
TCP: inuse:正在使用(正在侦听)的TCP套接字数量,其值不大于netstat –lnt | grep ^tcp | wc –l
TCP: orphan:不属于任何进程孤儿TCP连接数(无用、待销毁的TCP socket数)
TCP: tw:等待关闭的TCP连接数,其值等于TIME_WAIT状态链路数
TCP:alloc(allocated):已分配(已建立、已申请到sk_buff)的TCP套接字数量。其值等于netstat –ant | grep ^tcp | wc –l
TCP: mem:套接字缓冲区使用量(单位页大小),对应系统所有tcp链路的内存开销。
UDP: inuse:正在使用的UDP套接字数量
RAW:单纯IP层socket
FRAG:使用的IP段数量