终究还是败给了腾讯,秒挂了。。。
共 16765字,需浏览 34分钟
·
2024-06-05 17:00
大家好,我是小林。
虽然今天 520 日子有点特殊,但是还是雷打不动给大家分享后端面经
这次分享是腾讯后端面经,面试接近 1 小时,问了非常多的问题,涵盖Linux、数据库、C++、操作系统、计算机网络。
我把比较有意思的题目摘出来跟大家解析一波,这场面试 70%的考察内容,都是Linux相关的,基本追着 Linux 命令拷打了,同学对于 Linux 没有准备太多,结果很不幸,直接秒挂了。
还是比较难度的,对 linux 不太熟悉的话,基本一问一个懵。
Linux
Linux 服务器当中如何查看负载情况?通过什么指标进行查看?
通常我们发现系统变慢时,我们都会执行top或者uptime命令,来查看当前系统的负载情况,比如像下面,我执行了uptime,系统返回的了结果,最后一个就是系统平均负载的情况。
Load Average的三个数字,依次则是过去1分钟、5分钟、15分钟的平均负载。可以通过观察这三个数字的大小,可以简单判断系统的负载是下降的趋势还是上升的趋势。负载值一般不超过cpu核数的1-1.5倍,如果超过1.5倍,那就要重视,此时会严重影响系统。
-
如果 load average: 1.00, 5.00, 10.00 三个数字依次增大,则说明在过去的 1 分钟系统的负载比过去 15 分钟系统的负载小,表明系统的负载是下降的趋势。 -
如果 load average: 10.00, 5.00, 1.00 三个数字依次降低,则说明在过去的 1 分钟系统的负载比过去 15 分钟系统的负载大,表明系统的负载是上升的趋势。 -
如果 load average: 0.07, 0.04, 0.0 三个数字基本相同,或者相差不大, 表明系统的负载是平稳的。
平均负载是指单位时间内,处于可运行状态和不可中断状态的进程数。所以,它不仅包括了正在使用 CPU 的进程,还包括等待 CPU 和等待 I/O 的进程。
而 CPU 使用率,是单位时间内 CPU 繁忙情况的统计,跟平均负载并不一定完全对应。比如:
-
CPU 密集型进程,使用大量 CPU 会导致平均负载升高,此时这两者是一致的; -
I/O 密集型进程,等待 I/O 也会导致平均负载升高,但 CPU 使用率不一定很高; -
大量等待 CPU 的进程调度也会导致平均负载升高,此时的 CPU 使用率也会比较高。
我们现在很清楚的知道导致平均负载高的情况,不只是看 CPU 的使用率,也要观察系统 I/O 等待时间高不高。
当发现平均负载升高时,可以使用 mpstat 命令查看 CPU 的性能。
# -P ALL 表示监控所有CPU,后面数字1表示间隔1秒后输出一组数据
$ mpstat -P ALL 1
Linux 2.6.32-431.el6.x86_64 (lzc) 11/05/2019 _x86_64_ (2 CPU)
07:51:45 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle
07:51:50 PM all 42.90 0.00 49.39 0.41 0.00 4.56 0.00 0.00 2.74
07:51:50 PM 0 44.38 0.00 48.67 0.41 0.00 2.86 0.00 0.00 3.68
07:51:50 PM 1 41.57 0.00 49.80 0.40 0.00 6.43 0.00 0.00 1.81
从上面发现
-
CPU 的用户层(%usr)使用率高达45%左右; -
CPU 的系统层(%sys)使用率高达50%左右; -
CPU 的 I/0 - 等待(%iowait)占用率为0.41%; -
CPU 的空闲率(%idle)只有2~3%。
可以推断出是由于 CPU 使用率导致平均负载升高的情况。假设只有 CPU 的I/0 等待(%iowait)占用率高,CPU 用户层和系统层使用率很轻松,那么导致平均负载升高的原因就是 iowait 的升高。判断了是因为 CPU 使用率升高还是 iowait 升高导致平均负载升高后,我们还需要定位是哪个进程导致的。可以用 pidstat 来查询:
# 间隔1秒后输出一组数据,-u表示CPU指标
$ pidstat -u 1
08:07:55 PM PID %usr %system %guest %CPU CPU Command
08:07:56 PM 4 0.00 1.00 0.00 1.00 0 ksoftirqd/0
08:07:56 PM 9 0.00 1.00 0.00 1.00 1 ksoftirqd/1
08:07:56 PM 11 0.00 16.00 0.00 16.00 0 events/0
08:07:56 PM 12 0.00 20.00 0.00 20.00 1 events/1
08:07:56 PM 616 7.00 6.00 0.00 13.00 1 pppoe
08:07:56 PM 2745 6.00 6.00 0.00 12.00 1 pppoe
可以发现是 events/0 和 events/1 内核进程 CPU 使用率非常高,所以可能这两个进程导致平均负载升高。
top 命令和 free 命令都可以查看内存,有什么区别?
free 命令主要是查看系统的内存使用情况物理内存:
-
total:总物理内存大小 -
used:已使用的内存 -
free:未使用的内存 -
shared:共享内存大小 -
buff/cache:缓冲和缓存内存大小 -
available:当前可用的内存,考虑到了部分缓存可以被快速释放的情况
交换内存:
-
total:总交换内存大小 -
used:已使用的交换内存 -
free:未使用的交换内存
top 命令,除了会显示系统内存情况,还会显示系统任务情况、CPU使用情况、各进程状态等信息。
怎么判断服务器内存是否够用?如何查看服务器性能瓶颈是否是内存?
使用 free 命令查看内存使用情况
使用 free -m 命令可以查看内存的总体使用情况,输出结果会大致如下:
total used free shared buff/cache available
Mem: 7982 1746 2523 155 3703 5818
Swap: 2047 6 2041
关注以下几项:
-
used:已经使用的内存。 -
free:可用的空闲内存。 -
available:可用的内存,这包括了操作系统缓存,这个值更能代表实际可用内存。
如果 available 的值长期很低,可能表明内存不足。
通过观察是否频繁使用 Swap 空间
可以通过 free 命令观察 Swap 空间的使用情况:
Swap: 2047 6 2041
-
如果 Swap 空间使用过多(例如,接近 Swap total),说明物理内存不足。
检查 dmesg 输出是否有 OOM(Out of Memory)信息
查看 /var/log/messages 或者使用 dmesg 命令来查看系统日志,检查是否有 OOM(Out of Memory)错误。
dmesg | grep -i "out of memory"
如果存在 OOM 错误,说明内存不足是一个明显的问题。
使用 vmstat 观测内存使用状况
vmstat 是另一个强大的工具,可以帮助你监控系统的内存使用情况。执行 vmstat 1 每秒刷新一次:特别关注以下字段:
-
si(swap in)和 so(swap out):如果这两个值较高,说明系统频繁使用交换空间,表明物理内存可能不足。 -
free:空闲内存。 -
buff 和 cache:缓存和缓冲区的使用情况。
如何判断内存是否是满的情况?通过什么指标判断内存的使用率?
-
如果 free 和 available 都非常低,而 used 很高,内存基本上是满的。 -
Swap 使用频繁且使用率较高,那么内存基本上是满的。 -
如果 dmesg 日志,出现 OOM 相关信息,说明内存不足导致了一些进程被杀掉。 -
ps 或 top 显示大部分内存被少数几个进程占用,那么内存基本上是满的。
操作系统内存不足的时候会发生什么?
应用程序通过 malloc 函数申请内存的时候,实际上申请的是虚拟内存,此时并不会分配物理内存。
当应用程序读写了这块虚拟内存,CPU 就会去访问这个虚拟内存, 这时会发现这个虚拟内存没有映射到物理内存, CPU 就会产生缺页中断,进程会从用户态切换到内核态,并将缺页中断交给内核的 Page Fault Handler (缺页中断函数)处理。
缺页中断处理函数会看是否有空闲的物理内存,如果有,就直接分配物理内存,并建立虚拟内存与物理内存之间的映射关系。
如果没有空闲的物理内存,那么内核就会开始进行回收内存的工作,回收的方式主要是两种:直接内存回收和后台内存回收。
-
后台内存回收(kswapd):在物理内存紧张的时候,会唤醒 kswapd 内核线程来回收内存,这个回收内存的过程异步的,不会阻塞进程的执行。 -
直接内存回收(direct reclaim):如果后台异步回收跟不上进程内存申请的速度,就会开始直接回收,这个回收内存的过程是同步的,会阻塞进程的执行。
如果直接内存回收后,空闲的物理内存仍然无法满足此次物理内存的申请,那么内核就会放最后的大招了 ——触发 OOM (Out of Memory)机制。
OOM Killer 机制会根据算法选择一个占用物理内存较高的进程,然后将其杀死,以便释放内存资源,如果物理内存依然不足,OOM Killer 会继续杀死占用物理内存较高的进程,直到释放足够的内存位置。申请物理内存的过程如下图:
系统内存紧张的时候,就会进行回收内存的工作,那具体哪些内存是可以被回收的呢?主要有两类内存可以被回收,而且它们的回收方式也不同。
-
文件页(File-backed Page):内核缓存的磁盘数据(Buffer)和内核缓存的文件数据(Cache)都叫作文件页。大部分文件页,都可以直接释放内存,以后有需要时,再从磁盘重新读取就可以了。而那些被应用程序修改过,并且暂时还没写入磁盘的数据(也就是脏页),就得先写入磁盘,然后才能进行内存释放。所以,回收干净页的方式是直接释放内存,回收脏页的方式是先写回磁盘后再释放内存。 -
匿名页(Anonymous Page):这部分内存没有实际载体,不像文件缓存有硬盘文件这样一个载体,比如堆、栈数据等。这部分内存很可能还要再次被访问,所以不能直接释放内存,它们回收的方式是通过 Linux 的 Swap 机制,Swap 会把不常访问的内存先写到磁盘中,然后释放这些内存,给其他更需要的进程使用。再次访问这些内存时,重新从磁盘读入内存就可以了。
文件页和匿名页的回收都是基于 LRU 算法,也就是优先回收不常访问的内存。LRU 回收算法,实际上维护着 active 和 inactive 两个双向链表,其中:
-
active_list 活跃内存页链表,这里存放的是最近被访问过(活跃)的内存页; -
inactive_list 不活跃内存页链表,这里存放的是很少被访问(非活跃)的内存页;
越接近链表尾部,就表示内存页越不常访问。这样,在回收内存时,系统就可以根据活跃程度,优先回收不活跃的内存。
是否看过内存替换的源码?有哪些方式?
传统的 LRU 算法存在 2 个问题:
-
「预读失效」导致缓存命中率下降 -
「缓存污染」导致缓存命中率下降
要解决这两个问题,可以使用 LFU 算法,根据页面被访问的频率来确定哪些页面是最不经常使用的,然后将这些页面替换出去。
也可以改造 LRU 算法,来避免这两个问题。Linux就是通过改造传统 LRU 算法来避免的,实现了 lru-k 算法,k 是 2。
为了避免「预读失效」造成的影响,Linux 对传统的 LRU 链表做了改进:
-
Linux 操作系统实现两个了 LRU 链表:活跃 LRU 链表(active list)和非活跃 LRU 链表(inactive list)。
但是如果还是使用「只要数据被访问一次,就将数据加入到活跃 LRU 链表头部」这种方式的话,那么还存在缓存污染的问题。为了避免「缓存污染」造成的影响,Linux 操作系统提高了升级为热点数据的门槛:
-
Linux 操作系统:在内存页被访问第二次的时候,才将页从 inactive list 升级到 active list 里。
怎么判断操作系统有没有在内存替换?或者说怎么统计内存替换的频率?
我们可以使用 sar -B 1 命令来观察:图中红色框住的就是后台内存回收和直接内存回收的指标,它们分别表示:
-
pgscank/s : kswapd(后台回收线程) 每秒扫描的 page 个数。 -
pgscand/s: 应用程序在内存申请过程中每秒直接扫描的 page 个数。 -
pgsteal/s: 扫描的 page 中每秒被回收的个数(pgscank+pgscand)。
如果系统时不时发生抖动,并且在抖动的时间段里如果通过 sar -B 观察到 pgscand 数值很大,那大概率是因为「直接内存回收」导致的。
top 命令查看是多少个 CPU 核心?
执行 top 命令之后,按数字 1,就能显示 CPU 有多少个核心了。
数据库
数据库为什么用 B+ 树?
MySQL 是会将数据持久化在硬盘,而存储功能是由 MySQL 存储引擎实现的,所以讨论 MySQL 使用哪种数据结构作为索引,实际上是在讨论存储引使用哪种数据结构作为索引,InnoDB 是 MySQL 默认的存储引擎,它就是采用了 B+ 树作为索引的数据结构。
要设计一个 MySQL 的索引数据结构,不仅仅考虑数据结构增删改的时间复杂度,更重要的是要考虑磁盘 I/0 的操作次数。因为索引和记录都是存放在硬盘,硬盘是一个非常慢的存储设备,我们在查询数据的时候,最好能在尽可能少的磁盘 I/0 的操作次数内完成。
二分查找树虽然是一个天然的二分结构,能很好的利用二分查找快速定位数据,但是它存在一种极端的情况,每当插入的元素都是树内最大的元素,就会导致二分查找树退化成一个链表,此时查询复杂度就会从 O(logn)降低为 O(n)。
为了解决二分查找树退化成链表的问题,就出现了自平衡二叉树,保证了查询操作的时间复杂度就会一直维持在 O(logn) 。但是它本质上还是一个二叉树,每个节点只能有 2 个子节点,随着元素的增多,树的高度会越来越高。
而树的高度决定于磁盘 I/O 操作的次数,因为树是存储在磁盘中的,访问每个节点,都对应一次磁盘 I/O 操作,也就是说树的高度就等于每次查询数据时磁盘 IO 操作的次数,所以树的高度越高,就会影响查询性能。
B 树和 B+ 都是通过多叉树的方式,会将树的高度变矮,所以这两个数据结构非常适合检索存于磁盘中的数据。
但是 MySQL 默认的存储引擎 InnoDB 采用的是 B+ 作为索引的数据结构,原因有:
-
B+ 树的非叶子节点不存放实际的记录数据,仅存放索引,因此数据量相同的情况下,相比存储即存索引又存记录的 B 树,B+树的非叶子节点可以存放更多的索引,因此 B+ 树可以比 B 树更「矮胖」,查询底层节点的磁盘 I/O次数会更少。 -
B+ 树有大量的冗余节点(所有非叶子节点都是冗余索引),这些冗余索引让 B+ 树在插入、删除的效率都更高,比如删除根节点的时候,不会像 B 树那样会发生复杂的树的变化; -
B+ 树叶子节点之间用链表连接了起来,有利于范围查询,而 B 树要实现范围查询,因此只能通过树的遍历来完成范围查询,这会涉及多个节点的磁盘 I/O 操作,范围查询效率不如 B+ 树。
B+ 树叶子节点用双向链表有什么缺点?
-
额外空间开销:维护叶子节点之间的双向链表需要额外的指针空间,增加了内存占用。 -
缓存不友好:如果缓存空间有限,双向链表会占用额外的缓存空间,降低了缓存命中率。
MySQL 和 OceanBase 的区别、优缺点?使用场景?
OceanBase 是一个高性能、高可用、分布式关系型数据库,它确实提供了一些相对于传统单实例 MySQL 数据库的优先级特性,比如高性能、高可用性以及可扩展性等。
-
高可用,对OceanBase而言,同一数据保存在多台(>=3)台服务器中的半数以上服务器上(例如3台中的2台),每一笔写事务也必须到达半数以上服务器才生效,因此当少数服务器故障时不会有任何数据丢失,能够做到RPO等于零。不仅如此,OceanBase底层实现的Paxos高可用协议,在主库故障后,剩余的服务器会很快自动选举出新的主库,实现自动切换,并继续提供服务,在实际的生产系统中做到RTO在20秒之内。
-
扩展能力。MySQL在数据库中间件的帮助下,可以通过分库分表来实现水平扩展。这种方案解决了传统数据库需要垂直扩展的通病,但还是存在相当的局限性,比如扩容和缩容困难、无法支持全局索引,数据一致性难以保证等。OceanBase则从数据库层面提供了真正意义上的水平扩展能力。OceanBase基于分布式系统实现,可以很方便地进行扩容和缩容,且能做到用户无感知。同时,OceanBase所具备的集群内动态负载均衡、多机分布式查询、全局索引的能力更进一步加强了其可扩展性。对于用户的分库分表方案,OceanBase提供了分区表和二级分区的功能,可以完全取而代之。
-
适合场景:对于不需要分布式部署、数据量较小、并发访问量适中的应用场景,MySQL的单实例部署足以满足需求,而无需引入分布式数据库的复杂性。对于分布式场景,需要高可用、扩展性的,可以使用OceanBase
操作系统
操作系统在进程调度的时候会做哪些事情?
-
根据特定的调度算法选择下一个要执行的进程,以确定运行哪个进程。常见的调度算法包括先来先服务(FCFS)、短作业优先(SJF)、时间片轮转(Round Robin)、优先级调度等。 -
选好进程之后,就会进行进程切换,在切换进程时,操作系统保存当前进程的状态(寄存器内容、程序计数器等)到进程控制块中,并恢复下一个进程的状态,实现进程之间的切换。
进程上下文有哪些?
各个进程之间是共享 CPU 资源的,在不同的时候进程之间需要切换,让不同的进程可以在 CPU 执行,那么这个一个进程切换到另一个进程运行,称为进程的上下文切换。
在详细说进程上下文切换前,我们先来看看 CPU 上下文切换
大多数操作系统都是多任务,通常支持大于 CPU 数量的任务同时运行。实际上,这些任务并不是同时运行的,只是因为系统在很短的时间内,让各个任务分别在 CPU 运行,于是就造成同时运行的错觉。
任务是交给 CPU 运行的,那么在每个任务运行前,CPU 需要知道任务从哪里加载,又从哪里开始运行。
所以,操作系统需要事先帮 CPU 设置好 CPU 寄存器和程序计数器。
CPU 寄存器是 CPU 内部一个容量小,但是速度极快的内存(缓存)。我举个例子,寄存器像是你的口袋,内存像你的书包,硬盘则是你家里的柜子,如果你的东西存放到口袋,那肯定是比你从书包或家里柜子取出来要快的多。
再来,程序计数器则是用来存储 CPU 正在执行的指令位置、或者即将执行的下一条指令位置。
所以说,CPU 寄存器和程序计数是 CPU 在运行任何任务前,所必须依赖的环境,这些环境就叫做 CPU 上下文。
既然知道了什么是 CPU 上下文,那理解 CPU 上下文切换就不难了。
CPU 上下文切换就是先把前一个任务的 CPU 上下文(CPU 寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务。
系统内核会存储保持下来的上下文信息,当此任务再次被分配给 CPU 运行时,CPU 会重新加载这些上下文,这样就能保证任务原来的状态不受影响,让任务看起来还是连续运行。
上面说到所谓的「任务」,主要包含进程、线程和中断。所以,可以根据任务的不同,把 CPU 上下文切换分成:进程上下文切换、线程上下文切换和中断上下文切换。
进程的上下文切换到底是切换什么呢?
进程是由内核管理和调度的,所以进程的切换只能发生在内核态。
所以,进程的上下文切换不仅包含了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的资源。
通常,会把交换的信息保存在进程的 PCB,当要运行另外一个进程的时候,我们需要从这个进程的 PCB 取出上下文,然后恢复到 CPU 中,这使得这个进程可以继续执行,如下图所示:大家需要注意,进程的上下文开销是很关键的,我们希望它的开销越小越好,这样可以使得进程可以把更多时间花费在执行程序上,而不是耗费在上下文切换。
网络
TCP 三次握手,客户端第三次发送的确认包丢失了发生什么?
客户端收到服务端的 SYN-ACK 报文后,就会给服务端回一个 ACK 报文,也就是第三次握手,此时客户端状态进入到 ESTABLISH 状态。
因为这个第三次握手的 ACK 是对第二次握手的 SYN 的确认报文,所以当第三次握手丢失了,如果服务端那一方迟迟收不到这个确认报文,就会触发超时重传机制,重传 SYN-ACK 报文,直到收到第三次握手,或者达到最大重传次数。
注意,ACK 报文是不会有重传的,当 ACK 丢失了,就由对方重传对应的报文。
举个例子,假设 tcp_synack_retries 参数值为 2,那么当第三次握手一直丢失时,发生的过程如下图:具体过程:
-
当服务端超时重传 2 次 SYN-ACK 报文后,由于 tcp_synack_retries 为 2,已达到最大重传次数,于是再等待一段时间(时间为上一次超时时间的 2 倍),如果还是没能收到客户端的第三次握手(ACK 报文),那么服务端就会断开连接。
服务端发送第二个报文后连接的状态进入什么状态
syn_rcvd 状态
三次握手和 accept 是什么关系?accept 做了哪些事情?
tcp 完成三次握手后,连接会被保存到内核的全连接队列,调用 accpet 就是从把连接取出来给用户程序使用。
客户端发送的第一个 SYN 报文,服务器没有收到怎么办?
当客户端想和服务端建立 TCP 连接的时候,首先第一个发的就是 SYN 报文,然后进入到 SYN_SENT 状态。在这之后,如果客户端迟迟收不到服务端的 SYN-ACK 报文(第二次握手),就会触发「超时重传」机制,重传 SYN 报文,而且重传的 SYN 报文的序列号都是一样的。不同版本的操作系统可能超时时间不同,有的 1 秒的,也有 3 秒的,这个超时时间是写死在内核里的,如果想要更改则需要重新编译内核,比较麻烦。当客户端在 1 秒后没收到服务端的 SYN-ACK 报文后,客户端就会重发 SYN 报文,那到底重发几次呢?在 Linux 里,客户端的 SYN 报文最大重传次数由 tcp_syn_retries内核参数控制,这个参数是可以自定义的,默认值一般是 5。
# cat /proc/sys/net/ipv4/tcp_syn_retries
5
通常,第一次超时重传是在 1 秒后,第二次超时重传是在 2 秒,第三次超时重传是在 4 秒后,第四次超时重传是在 8 秒后,第五次是在超时重传 16 秒后。没错,每次超时的时间是上一次的 2 倍。当第五次超时重传后,会继续等待 32 秒,如果服务端仍然没有回应 ACK,客户端就不再发送 SYN 包,然后断开 TCP 连接。所以,总耗时是 1+2+4+8+16+32=63 秒,大约 1 分钟左右。举个例子,假设 tcp_syn_retries 参数值为 3,那么当客户端的 SYN 报文一直在网络中丢失时,会发生下图的过程:具体过程:
-
当客户端超时重传 3 次 SYN 报文后,由于 tcp_syn_retries 为 3,已达到最大重传次数,于是再等待一段时间(时间为上一次超时时间的 2 倍),如果还是没能收到服务端的第二次握手(SYN-ACK 报文),那么客户端就会断开连接。
服务器收到第一个 SYN 报文,回复的 SYN + ACK 报文丢失了怎么办?
当服务端收到客户端的第一次握手后,就会回 SYN-ACK 报文给客户端,这个就是第二次握手,此时服务端会进入 SYN_RCVD 状态。第二次握手的 SYN-ACK 报文其实有两个目的 :
-
第二次握手里的 ACK, 是对第一次握手的确认报文; -
第二次握手里的 SYN,是服务端发起建立 TCP 连接的报文;
所以,如果第二次握手丢了,就会发生比较有意思的事情,具体会怎么样呢?因为第二次握手报文里是包含对客户端的第一次握手的 ACK 确认报文,所以,如果客户端迟迟没有收到第二次握手,那么客户端就觉得可能自己的 SYN 报文(第一次握手)丢失了,于是客户端就会触发超时重传机制,重传 SYN 报文。然后,因为第二次握手中包含服务端的 SYN 报文,所以当客户端收到后,需要给服务端发送 ACK 确认报文(第三次握手),服务端才会认为该 SYN 报文被客户端收到了。那么,如果第二次握手丢失了,服务端就收不到第三次握手,于是服务端这边会触发超时重传机制,重传 SYN-ACK 报文。在 Linux 下,SYN-ACK 报文的最大重传次数由 tcp_synack_retries内核参数决定,默认值是 5。
# cat /proc/sys/net/ipv4/tcp_synack_retries
5
因此,当第二次握手丢失了,客户端和服务端都会重传:
-
客户端会重传 SYN 报文,也就是第一次握手,最大重传次数由 tcp_syn_retries内核参数决定; -
服务端会重传 SYN-ACK 报文,也就是第二次握手,最大重传次数由 tcp_synack_retries 内核参数决定。
举个例子,假设 tcp_syn_retries 参数值为 1,tcp_synack_retries 参数值为 2,那么当第二次握手一直丢失时,发生的过程如下图:具体过程:
-
当客户端超时重传 1 次 SYN 报文后,由于 tcp_syn_retries 为 1,已达到最大重传次数,于是再等待一段时间(时间为上一次超时时间的 2 倍),如果还是没能收到服务端的第二次握手(SYN-ACK 报文),那么客户端就会断开连接。 -
当服务端超时重传 2 次 SYN-ACK 报文后,由于 tcp_synack_retries 为 2,已达到最大重传次数,于是再等待一段时间(时间为上一次超时时间的 2 倍),如果还是没能收到客户端的第三次握手(ACK 报文),那么服务端就会断开连接。
假设客户端重传了 SYN 报文,服务端这边又收到重复的 SYN 报文怎么办?
会继续发送第二次握手报文。
点击关注公众号,阅读更多精彩内容