纯干货!Linux网络内核探究

1,031 阅读15分钟

 

Linux内核参数调优

目录

Linux内核参数调优

---Linux虚拟文件系统

---/proc文件系统目录分类:

内核参数配置方式

---临时配置内核参数有两种方式:

---永久配置内核参数方式:

网络内核参数优化

---常用参数含义以及优化规则


Linux虚拟文件系统

操作系统运行起来后,还有很多工作需要跟内核交互,Linux就使用/proc虚拟文件系统来进行用户空间和内核空间的通信。在/proc文件系统中,可以将对虚拟文件的读写作为与内核通信的一种手段,但是与普通文件不同的是,这些虚拟文件的内容是动态创建的。使用命令查看这些文件会返回信息,但是文件本身大小为0字节,因为这些都是驻留在内存中的文件。

/proc文件系统目录分类:

  • /proc/sys/net:网络相关参数
  • /proc/sys/kernel:内核相关参数
  • /proc/sys/vm:内存相关参数
  • /proc/sys/fs:文件系统相关参数

/sys目录主要存放硬件设备的驱动信息,可以通过/sys/block优化磁盘I/O

内核参数配置方式

临时配置内核参数有两种方式:

使用cat命令查看内核参数,使用echo配置内核参数。

 # 查看Linux代理转发功能,默认值0禁止IP转发,1开启转发功能
 cat /proc/sys/net/ipv4/ip_forward
 # 将参数修改为1,即开启转发功能
 echo "1" >/proc/sys/net/ipv4/ip_forward

使用sysctl命令,使用/proc/sys目录树中的文件作为参数名

 # 查看参数
 sysctl net.ipv4.ip_forward
 # 修改参数,直接生效
 sysctl -w net.ipv4.ip_forward

永久配置内核参数方式:

 # 编辑/etc/sysctl.conf,在文件中添加参数配置
 vi /etc/sysctl.conf
 net.ipv4.ip_forward = 1
 # 立即生效命令
 sysctl -p

网络内核参数优化

Linux作为Web服务器时,必须要进行网络内核参数优化。就像我们把一台出厂设置的手机,设置成符合我们使用习惯一样,这样能极大提升使用效率。

常用参数含义以及优化规则

1./proc/sys/net/ipv4/tcp_syncookies

tcp_syncookies参数告诉内核开启SYN Cookies,用于防御 SYN Flood 攻击。0表示关闭SYN Cookies;默认值1表示在连接队列已满时启用SYN Cookies,2表示始终使用SYN Cookies

SYN Flood攻击就是来自伪造地址的一系列SYN包。当Server端收到攻击方发送的SYN包后,会响应一个SYN+ACK包,然后服务端会保存状态信息,同时等待Client端的ACK包。因为攻击方使用的是假IP,所以不会响应ACK包给Server端。

SYN_FLOOD

被攻击的Server端维持了大量的SYN_RECV状态的半连接,并且会默认重发5次SYN+ACK包,大量的半连接会塞满TCP等待连接队列,服务器资源被耗尽,导致合法连接也无法建立。

SYN Cookie开启后,如果连接队列已满,不会直接拒绝新连接,而是由特定算法生成SYN-ACK响应中的TCP序列号(原来是客户端随机生成的),n,而是将这些信息保存在SYN+ACK的初始序号和时间戳中。对于正常的连接,这些信息会随着ACK报文被带回来。

SYN Cookie的缺点:

  • MSS(Maximum Segment Size,最大报文段大小)只有3位,因此最多只能使用 8MSS值。
  • 算法计算带来的消耗
  • 服务器必须拒绝客户端SYN报文中的其他只在SYNSYN+ACK中协商的选项,原因是服务器没有地方可以保存这些选项,比如Wscale(窗口缩放因子)和SACK(Selective ACK)。

注意,如果您在日志中看到 SYN 洪水警告,但查明它们是由于合法连接过载而发生的,您应该调整其他参数,直到此警告消失。请参阅:tcp_max_syn_backlog、tcp_synack_retries、tcp_abort_on_overflow。

2./proc/sys/net/ipv4/tcp_syn_retries

tcp_syn_retries参数告诉内核服务器作为Client向外建立Socket连接,且Server端未返回SYN+ACK导致超时,内核要发送多少个SYN连接请求才决定放弃。此值默认值是6(CentOS7),则超时时间为127秒(2^6-1),不应大于255,建议设置为2。

tcp3次握手模型.jpg

3./proc/sys/net/ipv4/tcp_synack_retries

tcp_synack_retries参数告诉内核被动建立连接时,重传SYN-ACK的次数。默认值是5,最大值不应超过255。Server端第一次发送SYN-ACK间隔1秒无应答后,进行第一次重传SYN-ACK,第5次重传SYN-ACK需要间隔31秒,第五次重传还需要等待63秒后发生最终超时,内核决定放弃连接。(2^1-1=1,2^2-1=3,2^3-1=7,2^4-1=15,2^5-1=31,2^6-1=63。共120秒)

4./proc/sys/net/ipv4/tcp_keepalive_time

tcp_keepalive_time参数告诉内核当TCP连接空闲时(简单的ACK包不被视为数据)保持存活的时间。默认7200秒后会发送第一个keepalive探针。可以通过调小达到节省服务器资源的目的。建议修改为300秒,设置为0表示禁用TCP保活时间。

传输层协议TCP的keepalive与应用层HTTP协议的keep-alive的写法和概念都不同。TCP的keepalive是在ESTABLISH状态的时候,双方空闲没有数据传输,多次发送心跳包检测连接是否存活(如果每次返回的都是RST,而不是ACK,则释放当前连接)。而HTTP的keep-alive说的是如何避免进行重复的TCP三次握手和四次挥手的环节,目的在于短时间内在同一个连接上进行多次请求/相应。

需要注意两点:

  • keepalive只能检测连接是否存活,不能检测连接是否可用。例如,某一方发生了死锁,无法在连接上进行任何读写操作,但是操作系统仍然可以响应网络层keepalive包。
  • keepalive并不是默认开启的,在Linux系统上没有全局的选项去开启TCP的keepalive。开启keepalive的应用必须在TCP的Socket中单独开启。

5./proc/sys/net/ipv4/tcp_keepalive_probes

tcp_keepalive_probes参数用来设置TCP连接发送多少个探针来决定TCP连接已经断开。默认值为9。当TCP连接空闲tcp_keepalive_time时间后立即发送第一个探针,当发送第9个探针后等待tcp_keepalive_intvl秒,如果仍没有应答ACK则决定TCP连接已经断开。共耗时(tcp_keepalive_time+tcp_keepalive_intvl*tcp_keepalive_probes)秒。

发送探针有以下三种情况:

  • 对端接收到探针,以期望的ACK应答,保持连接。那么TCP连接空闲tcp_keepalive_time时间后,重新发送第一个探针。
  • 对端接收到探针,以RST应答,证明对端服务出现问题,于是发送RST终止当前连接,本端释放连接。
  • 没有收到应答,则间隔tcp_keepalive_intvl的时间再次发送,直到发送tcp_keepalive_probes个探针后仍然没有收到应答,则发送 RST包关闭该连接。

6./proc/sys/net/ipv4/tcp_keepalive_intvl

tcp_keepalive_intvl用来设置发送保活探针的间隔时间,默认值为75s。

7./proc/sys/net/ipv4/tcp_orphan_retries

当Client端Socket主动调用close()关闭TCP连接,会向Server端发送FIN数据包,如果没有受到ACK应答,Client端会根据tcp_orphan_retries值来指定重传数据包的次数,当发生了RTO(retransmission timeout)后,等待应答的时间以指数级增长,这期间Socket虽然调用close()方法,但是没有进入CLOSED状态,把此时的没有释放资源的Socket称为orphaned socket。如果仍然没有任何确认应答返回,强制关闭连接。

tcp四次挥手模型.jpg

tcp_orphan_retries会影响本地关闭TCP连接的超时时间,从而导致系统内有大量占用资源的orphaned socket,如果服务器负载量比较大,建议配置一个较小的值。需要注意的是,此参数默认配置为0,但是0不代表不进行重传,而是重传8次。因为在内核源码中,当retries为0时,会被赋值8。

  /* Calculate maximal number or retries on an orphaned socket. */
 static int tcp_orphan_retries(struct sock *sk, int alive)
 {
          int retries = sysctl_tcp_orphan_retries; /* May be zero. */
  
          /* We know from an ICMP that something is wrong. */
          if (sk->sk_err_soft && !alive)
                  retries = 0;
  
          /* However, if socket sent something recently, select some safe
           * number of retries. 8 corresponds to >100 seconds with minimal
           * RTO of 200msec. */
          if (retries == 0 && alive)
                  retries = 8;
          return retries;
 }

8./proc/sys/net/ipv4/tcp_fin_timeout

tcp_fin_timeout表示孤立连接(orphaned socket)在进入FIN_WAIT_2状态后,等待最终的FIN数据包的时间长度。默认值为60 秒,适当改小这个值可以加速回收。

9./proc/sys/net/ipv4/tcp_max_syn_backlog

tcp_max_syn_backlog参数用来设置半连接队列(SYN_RCVD)的上限,默认值为128。当连接请求到来,会将当前SYN_RCVD状态的连接数量与tcp_max_syn_backlog的值比较,如果超出tcp_max_syn_backlog的值,则拒绝连接。

对于低内存的机器tcp_max_syn_backlog的值是128,它会根据机器的内存增加而增加。如果服务器负载很高,需要增大tcp_max_syn_backlog来容纳更多半连接。

注意,启用tcp_syncookies时,此设置将被忽略。

10./proc/sys/net/ipv4/tcp_tw_recycle

tcp_tw_recycle是快速回收TIME_WAIT状态的Socket的开关,默认为0,表示关闭状态;设置为1时表示开启。

tcp_tw_recycle关闭时,主动关闭TCP连接的一方会进入TIME_WAIT状态,在等待2*MSL(MSL默认为60秒,通过/proc/sys/net/ipv4/tcp_fin_timeout参数配置)时间后进入CLOSED状态。开启tcp_tw_recycle后,则只需等待一个重传时间(RTO)后就能够快速的释放这个链接。

通常TIME_WAIT状态的Socket是无害的,TIME_WAIT状态是为了保证关闭的TCP端口不被立即使用。因为当网络存在延迟时,某个端口被关闭后,网络中可能还有一些重传的报文段在发向这个端口,如果建立相同四元组的新连接,就会被延迟报文段影响,所以用2倍MSL时间来限制这个端口使用。

在开启tcp_tw_recycle选项后,当连接进入TIME_WAIT状态,会记录对应远端主机最后到达的时间戳(依赖net.ipv4.tcp_timestamps选项)。如果同样的主机有新的分节到达,且时间戳小于之前记录的时间戳,即视为无效,相应的数据包会被丢弃。

尤其在NAT网络中,TCP请求经过网关修改目的地址后转发给服务端,但客户端时间戳没有改变。那么如果NAT网络中多台服务器没有进行时间同步的话,

对于后端就会认为时间戳小的报文是应该丢弃的来自同一个连接的延迟报文。

需要注意的是,在高并发短连接的Web服务器上,最好增加更多的四元组数目来容纳更多的TIME_WAIT状态的Socket。如果后端使用了负载均衡器,尽量使用长连接来减少TIME_WAIT状态的Socket。只有确保网络环境不是NAT,才能使用tcp_tw_recycle。在Linux 4.12中取消了tcp_tw_recycle参数。

11./proc/sys/net/ipv4/tcp_tw_reuse

tcp_tw_reuse是复用TIME_WAIT状态Socket的开关,默认为0,表示关闭状态,设置为1时表示开启。

tcp_tw_reuse设置的是内核变量sysctl_tcp_tw_reuse,这个变量位于_tcp_v4_check_established函数中,有且仅有一条调用路径tcp_v4_hash_connect—>__tcp_v4_check_established。从下图注释可以看出tcp_v4_hash_connect方法是在Socket客户端发起连接时调用,所以tcp_tw_reuse参数只能在Socket客户端主动建立TCP连接时生效。

tcp_tw_reuse.jpg

当服务器作为客户端时,主动关闭TCP连接会产生大量TIME_WAIT状态的Socket。在启用tcp_tw_reuse后,内核会选取一个处于TIME_WAIT的连接复用。

RFC1323协议提供了一组TCP扩展,以提高带宽路径上的性能。除此之外,它还定义了一个新的TCP选项,其中包含两个四字节的时间戳字段。第一个是发送方的当前时钟时间戳,而第二个是从远程主机接收到的最新时间戳。

复用连接后,这条连接的时间被更新为当前的时间,当延迟的数据达到,延迟数据的时间是小于新连接的时间,所以,内核可以通过时间判断出,延迟的数据是否可以安全的丢弃。

12./proc/sys/net/core/netdev_max_backlog

netdev_max_backlog参数用于指定网络接口积压队列的大小,默认值为1000。如果网卡设备接收数据包的速度快于内核处理它们的速度,则使用该队列。如果这个队列太小,数据包会在接收方丢失,而不是在网络上丢失。适当调大此参数可以优化网络设备接收队列。

13./proc/sys/net/core/rmem_max、wmem_max

  • rmem_max:指定最大读取套接字缓冲区大小。为了最大限度地减少网络数据包丢失,该缓冲区必须足够大,以处理传入的网络数据包。默认值212992字节。
  • wmem_max:指定最大写入套接字缓冲区大小。为了最大限度地减少网络数据包丢失,此缓冲区必须足够大,以处理传出的网络数据包。,默认值212992字节。

需要注意的是,缓冲区如果设置很大,会给内核带来寻找和分配内存的负担,这可能导致数据包丢失。

14./proc/sys/net/ipv4/tcp_rmem、tcp_wmem

tcp_rmemtcp_wmem这两个参数用于配置内核自动对Socket缓冲区优化的能力。

  • tcp_rmem:指定用于TCP套接字的最小、默认和最大接收缓冲区大小。最大值不能大于net.core.rmem_max
 [root@localhost ~]# cat /proc/sys/net/ipv4/tcp_rmem
 4096    87380   6291456
  • tcp_wmem:指定用于TCP套接字的最小、默认和最大发送缓冲区大小。最大值不能大于net.core.wmem_max
 [root@localhost ~]# cat /proc/sys/net/ipv4/tcp_wmem
 4096    16384   4194304

Socket结构图:

img

15./proc/sys/net/core/somaxconn

somaxconn参数用来指定每个端口的Socket监听队列backlog的长度。默认值128。当一个请求尚未被处理时,它会进入backlog。而Socket Server可以一次性处理backlog中的所有请求,处理后的请求不再位于监听队列中。当Server处理请求速度较慢,会导致监听队列被填满后新来的请求被拒绝。

tcp_max_syn_backlog是指定所能接受SYN同步包的最大客户端数量,即半连接(SYN_RECV)上限;

somaxconn是指服务端所能accept即处理数据的最大客户端数量,即完成连接(ESTABLISHED)上限。

如果设置somaxconn大于tcp_max_syn_backlog,那么somaxconn会自动降为tcp_max_syn_backlog的值。

注意,在Java中SocketServer的backlog被默认指定为50,此时将选取min(somaxconn, backlog)较小的值作为请求连接上限。

image-20210830170605284.png

欢迎大家关注我,一起学习Linux!

qrcode.jpg