前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >终究还是败给了腾讯,秒挂了。。。

终究还是败给了腾讯,秒挂了。。。

作者头像
小林coding
发布2024-05-28 12:28:10
1340
发布2024-05-28 12:28:10
举报
文章被收录于专栏:小林coding小林coding

图解学习网站:https://xiaolincoding.com

大家好,我是小林。

虽然今天 520 日子有点特殊,但是还是雷打不动给大家分享后端面经

这次分享是腾讯后端面经,面试接近 1 小时,问了非常多的问题,涵盖Linux、数据库、C++、操作系统、计算机网络。

我把比较有意思的题目摘出来跟大家解析一波,这场面试 70%的考察内容,都是Linux相关的,基本追着 Linux 命令拷打了,同学对于 Linux 没有准备太多,结果很不幸,直接秒挂了。

还是比较难度的,对 linux 不太熟悉的话,基本一问一个懵。

Linux

Linux 服务器当中如何查看负载情况?通过什么指标进行查看?

通常我们发现系统变慢时,我们都会执行top或者uptime命令,来查看当前系统的负载情况,比如像下面,我执行了uptime,系统返回的了结果,最后一个就是系统平均负载的情况。

image.png

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 的性能。

代码语言:javascript
复制
# -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 来查询:

代码语言:javascript
复制
# 间隔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 命令可以查看内存的总体使用情况,输出结果会大致如下:

代码语言:javascript
复制
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 空间的使用情况:

代码语言:javascript
复制
Swap:           2047           6        2041
  • 如果 Swap 空间使用过多(例如,接近 Swap total),说明物理内存不足。

检查 dmesg 输出是否有 OOM(Out of Memory)信息

查看 /var/log/messages 或者使用 dmesg 命令来查看系统日志,检查是否有 OOM(Out of Memory)错误。

代码语言:javascript
复制
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。

image.png

为了避免「预读失效」造成的影响,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秒之内。

image.png

  • 扩展能力。MySQL在数据库中间件的帮助下,可以通过分库分表来实现水平扩展。这种方案解决了传统数据库需要垂直扩展的通病,但还是存在相当的局限性,比如扩容和缩容困难、无法支持全局索引,数据一致性难以保证等。OceanBase则从数据库层面提供了真正意义上的水平扩展能力。OceanBase基于分布式系统实现,可以很方便地进行扩容和缩容,且能做到用户无感知。同时,OceanBase所具备的集群内动态负载均衡、多机分布式查询、全局索引的能力更进一步加强了其可扩展性。对于用户的分库分表方案,OceanBase提供了分区表和二级分区的功能,可以完全取而代之。

image.png

  • 适合场景:对于不需要分布式部署、数据量较小、并发访问量适中的应用场景,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 状态

image.png

三次握手和 accept 是什么关系?accept 做了哪些事情?

tcp 完成三次握手后,连接会被保存到内核的全连接队列,调用 accpet 就是从把连接取出来给用户程序使用。

image.png

客户端发送的第一个 SYN 报文,服务器没有收到怎么办?

当客户端想和服务端建立 TCP 连接的时候,首先第一个发的就是 SYN 报文,然后进入到 SYN_SENT 状态。在这之后,如果客户端迟迟收不到服务端的 SYN-ACK 报文(第二次握手),就会触发「超时重传」机制,重传 SYN 报文,而且重传的 SYN 报文的序列号都是一样的。不同版本的操作系统可能超时时间不同,有的 1 秒的,也有 3 秒的,这个超时时间是写死在内核里的,如果想要更改则需要重新编译内核,比较麻烦。当客户端在 1 秒后没收到服务端的 SYN-ACK 报文后,客户端就会重发 SYN 报文,那到底重发几次呢?在 Linux 里,客户端的 SYN 报文最大重传次数由 tcp_syn_retries内核参数控制,这个参数是可以自定义的,默认值一般是 5。

代码语言:javascript
复制
# 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。

代码语言:javascript
复制
# 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 报文怎么办?

会继续发送第二次握手报文。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-05-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小林coding 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Linux
    • Linux 服务器当中如何查看负载情况?通过什么指标进行查看?
      • top 命令和 free 命令都可以查看内存,有什么区别?
        • 怎么判断服务器内存是否够用?如何查看服务器性能瓶颈是否是内存?
          • 如何判断内存是否是满的情况?通过什么指标判断内存的使用率?
            • 操作系统内存不足的时候会发生什么?
              • 是否看过内存替换的源码?有哪些方式?
                • 怎么判断操作系统有没有在内存替换?或者说怎么统计内存替换的频率?
                  • top 命令查看是多少个 CPU 核心?
                  • 数据库
                    • 数据库为什么用 B+ 树?
                      • B+ 树叶子节点用双向链表有什么缺点?
                        • MySQL 和 OceanBase 的区别、优缺点?使用场景?
                        • 操作系统
                          • 操作系统在进程调度的时候会做哪些事情?
                            • 进程上下文有哪些?
                            • 网络
                              • TCP 三次握手,客户端第三次发送的确认包丢失了发生什么?
                                • 服务端发送第二个报文后连接的状态进入什么状态
                                  • 三次握手和 accept 是什么关系?accept 做了哪些事情?
                                    • 客户端发送的第一个 SYN 报文,服务器没有收到怎么办?
                                      • 服务器收到第一个 SYN 报文,回复的 SYN + ACK 报文丢失了怎么办?
                                        • 假设客户端重传了 SYN 报文,服务端这边又收到重复的 SYN 报文怎么办?
                                        相关产品与服务
                                        云数据库 MySQL
                                        腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
                                        领券
                                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档