内核参数调优

/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
2
3
4
5
6
7
8
9
10
11
12
static void tcp_init_mem(void)
{
/* nr_free_buffer_pages()计算ZONE_DMA和ZONE_NORMAL的页数,
* 对于64位系统来说,其实就是所有内存了。
*/
unsigned long limit = nr_free_buffer_pages() / 8;
limit = max(limit, 128UL); /* 不能低于128页 */

sysctl_tcp_mem[0] = limit / 4 * 3; /* 最小值设为3/32的系统内存 */
sysctl_tcp_mem[1] = limit; /* 压力值设为1/8的系统内存 */
sysctl_tcp_mem[2] = sysctl_tcp_mem[0] * 2; /* 最大值设为3/16的系统内存 */
}

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)当设置的值的两倍val
2 < 最小值,则设置成最小值: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

通过setsockopt设置SO_SNDBUF、SO_RCVBUF这连个默认缓冲区的值,再用getsockopt获取设置的值,发现返回值是设置值的两倍。

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参数

  1. 采用sar -n DEV 1能更好的观察收发的速度,sar -n SOCK 1可以查看当前使用的socket数。
  2. 除了使用netstat查看tcp连接的收发缓冲区大小,还可以使用ss命令,另外/proc/net/tcp可以看到更详细的tcp连接信息,比如拥塞窗口,重传次数等。
  3. 可以在代码里使用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段数量

nephen wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
坚持原创技术分享,您的支持将鼓励我继续创作!