360 软件开发-C++ 一面

1. 自我介绍

2. 进程、线程、协程有什么区别

进程可以理解成资源分配的基本单位,每个进程通常有独立的虚拟地址空间、文件描述符表、堆栈等资源,进程之间隔离性强,但切换开销相对更大。线程是 CPU 调度的基本单位,同一进程内的线程共享地址空间和大部分进程资源,所以线程间通信更方便,但共享数据也意味着更容易出现竞争和同步问题。协程本质上是用户态的轻量级执行单元,它不是由操作系统内核直接调度,而是由用户态运行时或者程序自己控制切换。协程切换通常只涉及少量上下文保存,开销更低,适合高并发 I/O 密集场景。如果从工程角度看,进程更偏隔离,线程更偏并行,协程更偏高并发下的调度效率和编程模型表达能力。

3. 说说 TCP 三次握手的过程,两次握手为什么不行

答案:三次握手的过程是这样的:客户端先发送一个 SYN,表示自己想建立连接,同时带上自己的初始序列号。服务端收到后,返回 SYN + ACK,一方面表示收到了客户端的 SYN,另一方面也把自己的初始序列号发给客户端。客户端再回一个 ACK,表示自己收到了服务端的 SYN。到这里双方都确认了彼此的收发能力和初始序列号,连接建立完成。两次握手不行的核心原因是,服务端无法确认客户端是否真的收到了自己的那次确认。如果只做两次,服务端可能已经认为连接建立成功,但客户端未必知道。这样会导致连接状态不一致,还可能引入历史失效连接请求被误用的问题。所以第三次握手的意义不只是“礼貌确认一下”,而是让双方对连接建立这件事达成一致。

4. TIME_WAIT 状态的作用是什么,如果有大量 TIME_WAIT 怎么处理

答案:TIME_WAIT 主要有两个作用。第一个是确保最后一个 ACK 有机会被对方收到。如果主动关闭方发送的最后 ACK 丢失,被动关闭方还会重发 FIN,这时主动关闭方还可以重新回复 ACK。第二个是让旧连接中的延迟报文在网络中自然消失,避免它们影响后续使用相同四元组建立的新连接。如果线上出现大量 TIME_WAIT,首先要看这是不是业务模型导致的,比如短连接过多。如果是,可以优先考虑连接复用、长连接、连接池。另外还可以通过系统参数适当调优,比如缩短 TIME_WAIT 保留时间、扩大本地端口范围,但这类优化要谨慎,不能破坏协议语义。真正稳定的方案通常还是减少无意义短连接,而不是单纯依赖内核参数硬压。

5. 了解微服务架构吗,简单说说 CAP 理论

答案:微服务架构本质上是把一个大系统拆成一组职责更清晰、可以独立部署和演进的小服务。它的好处是模块边界更清晰、团队协作更灵活、局部迭代和扩缩容更方便,但代价是分布式系统复杂度会明显提升,比如服务发现、链路追踪、配置管理、熔断限流、分布式事务这些问题都会变得更重要。CAP 理论说的是在分布式系统里,一致性、可用性、分区容错性三者不能同时做到完美。分区容错在真实分布式环境里几乎是必须接受的前提,所以很多时候实际是在一致性和可用性之间做取舍。像强一致系统会更倾向 CP,优先保证数据一致;高可用系统会更倾向 AP,在网络分区时接受短暂不一致,后续再做收敛。

6. 了解过 Raft 协议吗,简单说说它解决了什么问题

答案:Raft 是一种分布式一致性协议,核心目标是在多个节点之间就“日志顺序”和“状态机执行结果”达成一致。它主要解决的是在分布式环境下,如何在节点宕机、网络抖动等情况下仍然选出一个领导者,并让集群中的多数节点对操作顺序达成一致。Raft 里通常有三种角色:Leader、Follower、Candidate。正常情况下由 Leader 接收客户端请求,把日志复制给其他节点,等多数节点确认后提交。如果 Leader 失联,Follower 超时后会发起选举,产生新的 Leader。相比 Paxos,Raft 的工程可理解性更强,所以很多分布式系统和 KV 存储都会采用它或者借鉴它的思想。

7. 说一下 select、poll、epoll 的区别,为什么高并发场景更常用 epoll

答案:selectpollepoll 都是 I/O 多路复用机制,目标是让单线程同时监听多个文件描述符。select 的问题在于有最大 fd 数量限制,而且每次调用都需要把整个 fd 集合从用户态拷到内核态,返回后还要遍历所有 fd。poll 去掉了 fd 数量上限,但本质上还是线性扫描,描述符很多时开销仍然不小。epoll 的优势在于把“关注哪些 fd”和“哪些 fd 就绪了”分开处理。注册时把 fd 挂到内核里,就绪事件通过内核维护的就绪链表返回,避免每次全量遍历。所以在高并发连接场景下,epoll 的可扩展性更好,尤其适合网络服务器。

代码:

#include <sys/epoll.h>
#include <unistd.h>
#include <iostream>
using namespace std;

int main() {
    int epfd = epoll_create1(0);
    if (epfd == -1) return -1;

    epoll_event events[16];
    int n = epoll_wait(epfd, events, 16, 1000);
    cout << "ready fds: " << n << endl;

    close(epfd);
    return 0;
}

8. 说一下 Reactor 和 Proactor 模型的区别

Reactor 模型强调的是“事件就绪通知”。也就是说,内核告诉你某个 fd 可读或可写了,真正的读写操作还是由应用程序自己去完成。Proactor 模型则更像“异步操作完成通知”。应用先发起异步读写,内核或底层系统在操作真正完成之后再通知应用。在 Linux 传统网络

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

C++ 常考面试题总结 文章被收录于专栏

本专栏系统梳理C++方向, 大中厂高频高频面试考点 , 内容皆来自真实面试经历,从基础语法、内存管理、STL与设计模式,到操作系统与项目实战,结合真实面试题深度解析,帮助开发者高效查漏补缺,提升技术理解与面试通过率,打造扎实的C++工程能力.

全部评论

相关推荐

评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务