腾讯面经分享(包含我的理解)

腾讯面经01

备注:wxg

我的理解,GPT4,视频学习

1、TCP网络建立连接的过程?

建立TCP连接,需要三次握手,缺一不可:

  • 一次握手:客户端发送带有 SYN(SEQ=x) 标志的数据包 -> 服务端,然后客户端进入 SYN_SEND 状态,等待服务器的确认;
  • 二次握手:服务端发送带有 SYN+ACK(SEQ=y,ACK=x+1) 标志的数据包 –> 客户端,然后服务端进入 SYN_RECV 状态
  • 三次握手:客户端发送带有 ACK(ACK=y+1) 标志的数据包 –> 服务端,然后客户端和服务器端都进入ESTABLISHED 状态,完成 TCP 三次握手。

2、TCP四次挥手的time_wait状态解释一下

假设是客户端向服务器发送关闭连接请求,此时会发送TCP首部FIN标志位置为1的报文,之后客户端进入FIN_WAIT_1的状态;
服务端收到该报文后,向客户端发送ACK应答报文,服务器进入CLOSED_WAIT状态;

客户端收到服务端的ACK应答报文后,进入FIN_WAIT_2状态。

等待服务器处理完数据,服务器再向客户端发送FIN报文,服务端随之进入LAST_ACK状态

客户端收到服务端的FIN报文后,回一个ACK应答报文,之后进入TIME_WAIT状态

服务器收到ACK应答报文后,就进入了CLOSED状态,至此服务器已经完成了连接的关闭

客户端经过2MSL一段时间后,自动进入CLOSED状态

TIME_WAIT的持续时间通常设置为2MSL,以确保TCP连接可靠地终止。

  1. 网络中可能存在的延迟报文:网络延迟可能导致报文在网络中滞留,2MSL等待时间确保了所有旧报文都已经在网络中消失,从而不会影响到后续的新连接。
  2. 双重保证连接双方都正确关闭:即使在一些极端情况下(如最后的ACK丢失),TIME_WAIT状态也为双方提供了足够的时间来确保连接被正确关闭。

因为服务器收到客户端断开连接的请求时,可能还有一些数据没有发完,这时先回复 ACK,表示接收到了断开连接的请求。等到数据发完之后再发 FIN,断开服务器到客户端的数据传送。

3、信号从网卡发送到内存的过程

信号从网卡发送到内存大概经过这些步骤:

1、网卡接受网络数据包

2、网卡向CPU发送中断

3、CPU响应网卡的中断

4、通过DMA传输数据到内存缓冲区

5、数据包拷贝到应用程序缓冲区

6、内核缓冲区交换

4、介绍一下TCP的滑动窗口,滑动窗口受哪些因素的影响

滑动窗口是用来实现流量控制的,滑动窗口的大小意味着接收方还有多大的缓冲区可以用来接收数据。发送方可以通过滑动窗口的大小来确定应该发送多少的字节数据,当滑动窗口为0时,发送方一般不能再发送数据了。

滑动窗口大小受接收方的buffer大小、发送方的buffer大小和网络拥塞状况影响

5、进程,线程和协程之间的区别,各自的优点

进程:

进程是操作系统分配资源的基本单位,具有完整独立的内存空间和系统资源;进程的创建、切换和终止开销都较大;优点是稳定安全,有良好的隔离机制,相互之间不干扰

线程:

线程是进程中的执行单元,一个进程可以包含多个线程,共享进程的地址空间和资源;线程的切换开销小于进程;优点资源开销相对小,可以提高并发性

协程:

协程是一种用户态实现的轻量级并发,由用户代码控制;协程之间通过协作调度实现并发,相互之间通过显式切换上下文,不需要操作系统内核参与。创建和切换开销极小;优点占用资源极小,无需线程切换的开销。

6、如果让你设计一个文件系统,你将如何组织磁盘结构

  1. 分区和引导块

  2. 索引节点区

  3. 数据区

  4. 索引块

  5. 目录结构

  6. 空闲磁盘空间管理

  7. 日志

  8. 缓存机制

  9. 访问控制

7、static关键字的作用?如果一个static全局变量在一个.h文件中声明,在两个.c文件中同时包含,那么这个变量是同一个变量吗

在C语言中:

  1. 局部变量的生命周期延长:当static用于函数内的局部变量时,该变量的生命周期从声明开始一直持续到程序结束,而不是像普通局部变量那样在函数调用结束时被销毁。这意味着即使函数执行结束,变量的值也会保持到下次函数调用。
  2. 限制变量或函数的作用域:当static用于全局变量或函数时,它将变量或函数的作用域限制在声明它的文件内,其他文件即使使用extern也无法访问到这个static全局变量或函数。这有助于避免命名冲突,并提供了一种形式的封装。

不是,每个.c文件将拥有该变量的一个单独的实例,它们之间互不影响。

8、#include的作用,发生在哪个阶段,具体做了什么?是单纯的文本替换吗

#include指令发生在编译过程的预处理阶段,在这个阶段,预处理器对源代码进行必要的转换和准备工作,使其准备好被编译器编译。对于#include指令,预处理器会查找指定的文件并将其内容直接插入到#include指令出现的位置。这种插入是文本级别的,可以视作一种“文本替换”。

9、C++如何实现多态的?底层机制是什么?

C++实现多态主要是通过虚函数(Virtual Functions)和继承。多态允许通过指向基类的指针或引用来调用派生类的方法,使得同一个接口可以有多个不同的实现。这在运行时(动态多态)和编译时(静态多态)都有应用。

动态多态是通过虚函数实现的,其底层机制依赖于虚函数表(Virtual Table,简称V-Table)和虚函数指针(V-Pointer)。

静态多态是通过函数重载和模板(Templates)实现的,编译器在编译期间根据函数的参数类型、数量或模板参数解析出调用哪个函数或实例化哪个模板。

10、为什么基类的析构函数需要是虚函数?不是会怎么样?

基类的析构函数需要声明为虚函数,主要是为了确保在使用基类指针删除派生类对象时,能够正确调用派生类的析构函数,从而实现完整的资源清理。

如果基类的析构函数不是虚函数,会发生以下情况:

  1. 对象切片问题(Object Slicing)

  2. 不符合面向对象设计原则

11、websocket/quic你了解吗

WebSocket是一种网络通信协议,提供了一种在单个TCP连接上进行全双工通信的方式。它是HTML5一部分,旨在在浏览器和服务器之间建立一个持久的、低延迟的双向通信通道。WebSocket允许服务器主动向客户端发送消息,解决了传统HTTP请求模型中服务器不能主动推送信息到客户端的限制。

QUIC是一种基于UDP的、提供可靠传输的网络协议。目的是减少网络延迟,提升网络通信的性能,特别是在移动网络和高延迟网络上。QUIC结合了TCP、TLS(传输层安全协议)和HTTP/2的特点,提供了比TCP更快的连接建立、加密通信、流控制、拥塞控制和多路复用。

  1. 减少连接延迟:QUIC通过在协议握手过程中同时进行连接建立和加密协商,减少了建立连接所需的往返时间。
  2. 多路复用:QUIC支持多个独立的数据流在单个连接上并行传输,解决了HTTP/2在TCP上的队头阻塞问题。
  3. 连接迁移:QUIC连接由连接ID标识,而非传统的IP地址+端口组合。这使得即使网络环境改变(如从Wi-Fi切换到移动数据),连接也能够继续保持。
  4. 前向纠错和快速重传:QUIC实现了改进的丢包恢复机制,可以更快地响应丢包和网络条件变化。

12、UDP一次最多传输多少字节数据

UDP数据载荷的最大理论长度是65535 - 8 = 65527字节。尽管UDP允许理论上最大65527字节的数据载荷,实际传输时还受到底层IP协议的MTU限制。大多数网络环境的MTU值大约在1500字节左右,对于IPv4,IP报头通常是20字节,如果还使用了IP分片,每个分片还会有自己的IP报头。对于不进行IP分片的单个UDP数据报,扣除IP报头的20字节后,理论上最大的UDP数据载荷会是1480字节,以避免在网络中的分片,从而减少可能的丢包和重组错误。常见的做法是保持数据载荷在1500-20(IP报头)-8(UDP报头) = 1472字节

13、quic是如何解决UDP无序问题的

QUIC如何解决数据无序问题的主要方法:

  1. 数据包编号(Packet Numbering)

  2. 多路复用(Multiplexing)

  3. 丢包检测和重传(Loss Detection and Retransmission)

  4. 连接迁移(Connection Migration)

  5. 确认和流量控制(Acknowledgements and Flow Control)

14、进程切换、线程切换和协程切换有什么区别

进程切换、线程切换和协程切换是操作系统中三种不同类型的上下文切换,它们各自有不同的开销和应用场景。了解它们之间的区别,有助于更好地理解操作系统的工作原理以及如何在实际应用中选择合适的并发模型。

进程切换

进程是操作系统分配资源和调度的基本单位,每个进程拥有独立的地址空间和系统资源(如文件句柄、内存等)。

  • 开销:进程切换涉及保存和恢复较多的上下文信息(包括CPU寄存器状态、内存管理信息、I/O状态等),并且需要操作系统介入进行地址空间的切换,因此开销较大。
  • 场景:适用于需要运行在隔离环境中的应用,或者各个任务之间没有共享数据、需要独立资源的场景。

线程切换

线程是进程中的执行单元,同一进程内的线程共享地址空间和部分系统资源,但每个线程有自己的执行栈和寄存器等上下文信息。

  • 开销:线程切换的开销小于进程切换,因为线程共享地址空间和系统资源,不需要切换内存页表,只需要保存和恢复线程的栈指针、寄存器等上下文信息。
  • 场景:适用于需要共享内存数据或文件等资源的并发任务,或者需要频繁进行上下文切换的场景。

协程切换

协程(Coroutine)是一种用户态的轻量级线程,它的切换完全由用户程序控制,而非操作系统。

  • 开销:协程切换的开销远小于线程和进程切换,因为协程切换不涉及内核态的操作,只需要在用户空间保存和恢复极少量的上下文信息(如协程栈的指针)。
  • 场景:适用于I/O密集型的应用,或者需要极高并发处理能力的场景,如Web服务器、数据库访问、网络服务等。

总结

  • 开销从大到小:进程切换 > 线程切换 > 协程切换。
  • 隔离性从强到弱:进程(最强) > 线程 > 协程(最弱)。
  • 控制权:进程和线程的切换由操作系统内核控制,而协程的切换由程序员在用户态控制,提供了更大的灵活性。

在设计系统和选择并发模型时,需要根据应用的特点和需求,在性能开销、资源共享、并发度等因素之间做出平衡选择。

15、用户态切换到内核态做了什么事情

用户态切换到内核态(也称为模式切换或上下文切换)是操作系统中的一个关键操作,它发生在用户程序请求操作系统提供服务(如系统调用)、发生中断或异常时。这个切换使得CPU从运行用户程序(用户态)转换为执行操作系统代码(内核态),以便执行诸如访问硬件资源、进行进程调度或处理中断等操作。用户态切换到内核态涉及以下主要步骤:

1. 保存用户态上下文

当发生用户态到内核态的切换时,操作系统首先需要保存当前用户程序的状态,以便之后能够恢复执行。这包括保存CPU寄存器的状态、程序计数器(PC)的值等,确保用户程序的执行状态不会丢失。

2. 切换堆栈

操作系统为内核态运行和用户态运行提供了不同的堆栈。切换到内核态时,会切换到内核堆栈。这是因为内核代码执行时可能需要使用不同的数据结构和栈空间,且内核堆栈通常在内核地址空间中,而不是用户地址空间。

3. 提升权限和切换地址空间

CPU将提升执行权限到内核级别,以允许访问受保护的内核资源和硬件设备。如果操作系统使用了硬件辅助的虚拟内存管理,还可能涉及到地址空间的切换,确保CPU访问的是内核的地址空间。

4. 执行内核代码

一旦切换到内核态,CPU开始执行相应的内核代码,这可能是系统调用的处理函数、中断处理程序或异常处理程序等。

5. 返回用户态

内核任务完成后,操作系统会准备返回用户态,这涉及到将CPU的执行权限降低回用户级别,恢复用户程序的上下文(包括寄存器状态、程序计数器等),并切换回用户堆栈和地址空间,最后跳转回用户程序继续执行。

  • 10
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星空有大海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值