C++后端工程师面试宝典 - 第4节
4. 操作系统与网络基础:进程与线程、同步机制、TCP三次握手、IO多路复用
本章定位:C++后端面试必考操作系统与网络基础,重点在进程线程区别、同步原语、TCP连接建立、IO多路复用模型。掌握回答框架可覆盖90%高频问题。
复习提示:将上图五个分支作为骨架,针对每个面试问题先定位到对应分支,再套用底部回答框架展开。
📌 面试问答1:进程与线程的区别
| 面试问题 | 请解释进程与线程的区别,并举例说明在多线程编程中如何选择。 |
| 考察点 | 是否理解OS级调度基本概念、上下文切换代价、资源共享方式。 |
| 回答框架 | ① 定义:进程是资源分配最小单位,线程是CPU调度最小单位。 ② 区别:进程有独立地址空间,线程共享进程资源;进程切换开销大(页表、TLB),线程切换轻量;进程间通信需IPC,线程间直接读写共享内存。 ③ 场景:计算密集型+多核用线程池;需要强隔离用多进程。 |
| 示例回答 | 进程拥有独立的虚拟地址空间和系统资源,线程是进程内的执行流,共享代码段和数据段。线程切换只需保存寄存器状态,而进程切换需要切换页表,开销高。例如在Web服务器中,用线程处理请求可降低延迟,但若要运行不可信插件,用进程隔离更安全。 |
| 可能追问 | ① 什么是协程?与线程的差异?② Linux下如何创建一个新进程?fork()的写时复制原理?③ 多线程中如何保证数据一致性? |
| 避坑提醒 | ❌ 不要混淆“线程比进程快”为结论;实际在简单计算任务中线程优势明显,但在需要大量fork的场景下进程也有性能优势。明确说“线程切换快”但也要提共享资源同步成本。 |
📌 面试问答2:解释TCP三次握手过程
| 面试问题 | 描述TCP三次握手的过程,并说明为什么需要第三次握手。 |
| 考察点 | 对TCP状态转移的理解、协议设计的可靠性思考、对失效连接的控制。 |
| 回答框架 | ① 流程:客户端发送SYN(seq=x)→服务端回复SYN+ACK(seq=y, ack=x+1)→客户端发送ACK(ack=y+1)。 ② 原因:第三次握手防止已失效的SYN请求连接建立,保证双方都确认对方接收能力正常。 ③ 状态:客户端SYN_SENT→ESTABLISHED;服务端LISTEN→SYN_RCVD→ESTABLISHED。 |
| 示例回答 | 三次握手确保双方协商初始序列号并确认收发能力。如果只有两次,服务端无法区分客户端是否收到了自己发送的SYN+ACK,可能导致服务端持续等待。第三次ACK是客户端对服务端序列号的确认,这样连接才可靠。 |
| 可能追问 | ① 什么是半连接队列和全连接队列?② 如果客户端发送SYN后宕机,服务端会一直等待吗?③ 四次挥手为什么是四次? |
| 避坑提醒 | ❌ 不要只背流程;必须说清楚“确认双方收发能力”及“防止历史连接”。面试官常会深入问SYN Flood和backlog配置,提前准备。 |
📌 面试问答3:IO多路复用select/poll/epoll对比
| 面试问题 | select、poll、epoll的区别是什么?epoll为什么性能更好? |
| 考察点 | 对IO模型的理解、系统调用的性能差异、大规模并发时的瓶颈分析。 |
| 回答框架 | ① 数据结构:select使用固定长度fd_set(1024限制);poll使用pollfd数组,无上限;epoll使用红黑树+双向链表。 ② 触发方式:select/poll水平触发;epoll支持边缘触发(ET)和水平触发(LT)。 ③ 效率:select/poll每次需拷贝全部fd到内核并线性遍历;epoll通过回调只返回就绪fd,O(1)复杂度。 |
| 示例回答 | select有描述符上限(默认1024),每次调用需将fd集合从用户态拷贝到内核态,遍历所有fd;epoll使用事件驱动机制,内核通过回调将就绪fd放入链表,用户通过epoll_wait直接获取,无上限且避免无关遍历。因此epoll在处理大量并发连接时性能远优于select。 |
| 可能追问 | ① epoll的LT和ET模式有什么区别?何时用ET?② 如何解决epoll惊群问题?③ Reactor模式如何用epoll实现? |
| 避坑提醒 | ❌ 不要简单说“epoll比select快”,需要具体分析场景(少量连接下select可能更快因为epoll需要维护回调结构)。注意边缘触发(ET)必须配合非阻塞IO使用,否则会导致死锁。 |
同步机制核心对比
| 同步原语 | 作用域 | 特点 | 典型场景 | 注意 |
|---|---|---|---|---|
| 互斥锁(mutex) | 线程间 | 独占访问,可重入或非可重入 | 保护临界区数据 | 避免持有锁时调用可能引起阻塞的函数 |
| 条件变量(cond) | 线程间 | 配合mutex,等待/通知机制 | 生产者-消费者 | 必须与unique_lock配对使用 |
| 信号量(sem) | 线程/进程 | 计数型,控制并发访问数量 | 限制资源池大小 | 注意PV操作的原子性 |
| 读写锁(rwlock) | 线程间 | 读共享、写独占 | 写少读多场景 | 避免写者饥饿 |
| 原子操作(atomic) | 线程间 | 无锁,CAS原理 | 计数器、状态标志 | 不支持复杂数据结构 |
项目案例:高性能IM服务器
| 项目名称 | 基于epoll的即时通讯(IM)后端服务器 |
| 业务背景 | 为公司内部团队提供实时消息服务,支持文本、图片和文件传输,要求低延迟、高吞吐。 |
| 规模指标 | 同时在线用户约5万,日均消息量2000万条,P99延迟<10ms。 |
| 技术栈 | C++17 · epoll(ET模式) · 线程池 · protobuf · MySQL · Redis(缓存用户状态) |
| 核心难点 | ① 海量连接下epoll惊群优化(使用EPOLLEXCLUSIVE) ② 粘包问题:自定义消息头(4字节长度+1字节类型) ③ 线程池与epoll事件循环的协调(Reactor模式) |
| 面试高频追问 | ① epoll唤醒线程时如何避免惊群?→ 设置EPOLLEXCLUSIVE或用SO_REUSEPORT。 ② 如何处理消息乱序?→ 服务端通过序列号排序,客户端使用滑动窗口。 ③ 为什么选择protobuf而非json?→ 二进制序列化快,解析性能高。 ④ 数据库压力如何缓解?→ 写操作先入Redis队列,异步批量落库。 |
| 可迁移回答话术 | “我在IM项目中负责网络层开发,使用epoll ET+非阻塞IO实现高性能事件驱动。通过区分业务类型设计线程池模型,避免锁竞争。针对粘包问题设计了TLV消息格式,保证协议健壮性。这个架构也可用于其他高并发网关或游戏服务器。” |
避坑总提醒:操作系统与网络基础是面试翻车重灾区,务必手画三次握手状态图、写出epoll_ctl/epoll_wait的调用序列。面试时主动延展到项目落地场景(如在哪用过条件变量、如何用epoll优化吞吐),让考官看到你的工程深度。