Linux 随记
Tcp链接关闭
在linux中,一切皆文件,本身socket就是一种文件类型,内核会为每一个打开的文件创建file结构并维护指向改结构的引用计数,每一个进程结构中都会维护本进程打开的文件数组,数组下标就是fd,内容就指向上面的file结构,close本身就可以用来操作所有的文件,做的事就是,删除本进程打开的文件数组中指定的fd项,并把指向的file结构中的引用计数减一,等引用计数为0的时候,就会调用内部包含的文件操作close,针对于socket,它内部的实现应该就是调用shutdown,只是参数是关闭读写端,从而比较粗暴的关闭连接。
第二个问题,信号的处理有三种,默认处理,忽略处理,自定义处理。默认处理就是采用系统自定义的操作,大部分信号的默认处理都是杀死进程,忽略处理就是当做什么都没有发生。
tw_reuse和SO_REUSEADDR区别
tcp_tw_reuse 是内核选项,主要用在连接的发起方。TIME_WAIT 状态的连接创建时间超过 1 秒后,新的连接才可以被复用,注意,这里是连接的发起方;
SO_REUSEADDR 是用户态的选项,SO_REUSEADDR 选项用来告诉操作系统内核,如果端口已被占用,但是 TCP 连接状态位于 TIME_WAIT ,可以重用端口。如果端口忙,而 TCP 处于其他状态,重用端口时依旧得到“Address already in use”的错误信息。注意,这里一般都是连接的服务方。
Linux中TCP报文重传
net.ipv4.tcp_syn_retries = 6
net.ipv4.tcp_synack_retries = 5
对于SYN报文,重传时间由上面两个内核参数控制
tcp_syn_retries Total_time = 2^0 + 2^1…+2^6=2^7-1=127s
net.ipv4.tcp_synack_retries = 2^6-1=63s
案例1:最常见的内核参数调优就是为了防止DoS(拒绝服务攻击)和DDoS(分布式拒绝服务攻击)。其中SYN Flood是当前最流行的DoS与DDoS的方式之一,这是一种利用TCP协议缺陷,发送大量伪造的TCP连接请求,常用假冒的IP或IP号段发来的海量请求连接的第一个握手包(SYN包),被攻击服务器回应第二个握手包(SYN+ACK包),因为对方是假冒IP,对方永远收不到包且不会回应第三个握手包。导致被攻击服务器保持大量SYN_RECV状态的“半连接”,并且会重试默认5次回应第二个握手包,塞满TCP等待连接队列,资源耗尽(CPU满负荷或内存不足),让正常的业务请求连接不进来。
-
开启syncookies后,当SYN队列满了后,TCP会通过原地址端口,目的地址端口和时间戳打造一个特别的Sequence Number(又叫cookie发回去,如果是攻击者则不会有响应,如果是正常连接则把这个SYNCookie发回来,然后服务器端可以通过cookie建立连接(即使不在SYN队列)。
案例2:在ha1.5版本的时候,由于还不支持热重载机制,当ha的配置文件变更时,一个临时的解决办法是通过开启SO_REUSEPORT,在ha reload过程中服务端直接拒绝所有的syn连接请求(比如通过iptables drop掉指定端口80的所有syn包),这样client端会根据上述内核参数在一定时间后重新发送syn请求,当服务端ha reload完成时,再允许syn连接
net.ipv4.tcp_retries1
net.ipv4.tcp_retries2
快速重传
快速重传机制「RFC5681」基于接收端的反馈信息来引发重传,而非重传计时器超时。
刚刚提到过,基于计时器的重传往往要等待很长时间,而快速重传使用了很巧妙的方法来解决这个问题:服务器如果收到乱序的包,也给客户端回复 ACK,只不过是重复的 ACK。就拿刚刚的例子来说,收到乱序的包 6,7,8,9 时,服务器全都发 ACK = 5。这样,客户端就知道 5 发生了空缺。一般来说,如果客户端连续三次收到重复的 ACK,就会重传对应包,而不需要等到计时器超时。
TCP用户超时UTO
可以在通过setsockopt来设置TCP用户超时时长,表示:已发出的数据包若在指定的时间没有被确认,则关闭连接,相当于单次的tcp_keepalive
需要注意的是该值会影响tcp报文重传机制中的RTO时间和待发送队列数据的最大保存时间
在netstat中查看各种链接状态的计时器
通过-o选项,查看Timer,如下
keepalive (6176.47/0/0)
<1st field> <2nd field>
该 1st field 可以有价值:
keepalive - 当套接字的keepalive定时器为ON时
on - 当套接字的重传定时器为ON时
tme wait - 主动断开连接的一方所处的tw状态2MSL计时器 ,linux一般为30 * 2s
off - 以上都不是ON
该 2nd field 有三个子字段:
(6176.47/0/0) -> (a/b/c)
a =定时器值(a = keepalive定时器,当1st field =“keepalive”时; a =重传定时器,当1st field =“on”时)
b =已发生的重新传输次数
c =已发送的keepalive探测器的数量
Linux IO多路复用
为什么select/poll/epoll等IO多路复用一般不搭配阻塞IO:https://www.zhihu.com/question/37271342
SIGPIPE信号理解
在netfilet中注册一个hook handler:
Linux中的进程描述符
一个连接对应一个socket,每个socket都有独立的缓冲区(内核缓冲区):套接字也是文件,当server端监听到有连接时,应用程序会请求内核创建Socket,Socket创建好后会返回一个文件描述符给应用程序,当有数据包过来网卡时,内核会通过数据包的源端口,源ip,目的端口等在内核维护的一个ipcb双向链表中找到对应的Socket,并将数据包赋值到该Socket的缓冲区,应用程序请求读取Socket中的数据时,内核就会将数据拷贝到应用程序的内存空间,从而完成读取Socket数据