米哈游 服务器开发方向 C++ 一面 面经
1. 协程和线程的本质区别是什么?协程的调度是由谁控制的?
线程由操作系统内核调度,切换需要陷入内核态,开销大(保存/恢复寄存器、栈、上下文)。协程是用户态的轻量级执行单元,调度由程序自身控制(协作式),切换只需保存少量寄存器,无系统调用开销。协程共享线程栈,适合高并发 IO 密集型场景,如游戏服务器的大量连接处理。
2. epoll 的 LT 和 ET 模式有什么区别?ET 模式下如何避免数据丢失?
LT(水平触发):只要 fd 可读/可写就持续通知,未处理完下次还会触发,使用简单但效率略低。ET(边缘触发):只在状态变化时触发一次,必须一次性把数据读完,否则不再通知。ET 下避免丢失的做法:将 fd 设为非阻塞,循环 read 直到返回 EAGAIN/EWOULDBLOCK 为止。
3. Reactor 和 Proactor 模式有什么区别?游戏服务器一般用哪种?
Reactor 是同步非阻塞模型,事件就绪后由应用层自己去读写数据(如 epoll + 非阻塞 IO)。Proactor 是异步模型,内核完成 IO 后才通知应用层(如 Windows IOCP)。游戏服务器在 Linux 上通常用 Reactor 模式,配合线程池处理业务逻辑,典型实现如 muduo、libevent。
4. TCP 四次挥手中,为什么 TIME_WAIT 状态需要等待 2MSL?
MSL 是报文最大生存时间。等待 2MSL 有两个原因:① 确保最后一个 ACK 能到达对端,若对端没收到会重发 FIN,2MSL 内能收到并重传 ACK;② 让本次连接的所有报文在网络中消亡,防止旧连接的延迟报文被新连接误收。
5. TCP 的滑动窗口和拥塞控制有什么关系?拥塞控制的四个阶段是什么?
滑动窗口是流量控制机制,由接收方通告窗口大小限制发送速率。拥塞控制是发送方根据网络状况自适应调整发送量,实际发送量 = min(拥塞窗口, 接收窗口)。四个阶段:① 慢启动:cwnd 指数增长;② 拥塞避免:cwnd 线性增长;③ 快重传:收到 3 个重复 ACK 立即重传;④ 快恢复:ssthresh = cwnd/2,cwnd = ssthresh 后进入拥塞避免。
6. UDP 如何实现可靠传输?说说 KCP 的核心思路。
UDP 本身不可靠,要实现可靠传输需在应用层加:序列号、ACK 确认、超时重传、滑动窗口、乱序重排。KCP 的核心思路:以带宽换延迟,取消 Nagle 算法,降低 RTO 计算的保守程度(1.5x 而非 2x),支持选择性重传(SACK),减少不必要的等待,适合游戏实时对战场景。
7. std::move 和 std::forward 的区别是什么?
std::move 无条件将左值转为右值引用,触发移动语义,转移资源所有权,避免深拷贝。std::forward 是完美转发,保留参数的原始值类别(左值还是右值),用于模板函数中将参数原封不动地传递给下一层,避免多余拷贝。本质上两者都只是 static_cast,不产生任何运行时代码。
8. 虚函数表(vtable)的布局是怎样的?多继承时 vtable 有几张?
每个含虚函数的类有一张 vtable,存放虚函数指针数组。对象内存开头存放 vptr 指向该表。单继承时子类复用父类 vtable 并覆盖重写的槽位。多继承时每个有虚函数的基类对应一张 vtable,子类对象内存中有多个 vptr,调用不同基类的虚函数时使用对应的 vptr,存在 thunk 调整 this 指针偏移。
9. weak_ptr 解决了什么问题?它是如何判断对象是否还活着的?
weak_ptr 解决 shared_ptr 循环引用导致内存泄漏的问题(如 A 持有 B 的 shared_ptr,B 持有 A 的 shared_ptr,引用计数永远不为 0)。weak_ptr 不增加强引用计数,通过控制块中的弱引用计数跟踪。判断对象是否存活:调用 lock() 尝试提升为 shared_ptr,若强引用计数已为 0 则返回空指针。
10. 内存池的设计思路是什么?游戏服务器为什么需要内存池?
内存池预先向系统申请一大块内存,按固定大小或分级管理,分配时直接从池中取,释放时归还到池而非还给系统。游戏服务器需要内存池的原因:① 频繁 new/delete 导致内存碎片;② 系统调用开销大,影响帧率和延迟;③ 内存池分配/释放是 O(1),且局部性好,缓存命中率高。常见实现:固定块内存池、tcmalloc、jemalloc。
11. std:: 和 在游戏服务器中如何选择?
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏系统梳理C++技术面试核心考点,涵盖语言基础、面向对象、内存管理、STL容器、模板编程及经典算法。从引用指针、虚函数表、智能指针等底层原理,到继承多态、运算符重载等OOP特性从const、static、inline等关键字辨析,到动态规划、KMP算法、并查集等手写实现。每个知识点以面试答题形式呈现,注重原理阐述而非冗长代码,帮助你快速构建完整知识体系,从容应对面试官提问,顺利拿下offer。