滴滴 C++后端开发 一面 面经

1. 进程与线程的区别是什么?

进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位。每个进程拥有独立的虚拟地址空间、文件描述符、信号处理等资源,进程间相互隔离,一个进程崩溃不会影响其他进程。线程则共享所属进程的地址空间和资源,创建和切换的开销远小于进程。

通信方式上,进程间通信(IPC)需要借助管道、消息队列、共享内存、Socket等机制,而线程间可以直接读写共享内存,但需要加锁保护。同步方面,线程使用互斥锁、条件变量、信号量等,进程间同步则更复杂。

实际开发中,Nginx采用多进程模型保证稳定性,Redis单线程处理命令避免锁竞争,而Java服务端通常用多线程处理并发请求。

2. C++11 协程(coroutine)的用户态线程是怎么实现的?

C++20才正式引入协程关键字 co_await / co_yield / co_return,C++11时代通常通过 ucontext、setjmp/longjmp 或第三方库(如 libco、boost.context)手动实现用户态协程。

核心思想是:每个协程拥有独立的栈空间,通过保存和恢复寄存器上下文(PC、SP等)来实现切换,整个过程在用户态完成,不涉及内核调度,切换开销极低(约几十纳秒,而线程切换约1~10微秒)。

协程的优势在于用同步的写法实现异步逻辑,避免回调地狱。滴滴、腾讯等公司的高并发服务端框架(如 libco)大量使用协程来处理网络IO,单机可支撑数十万并发连接。

3. 什么是 Reactor 模型?

Reactor 是一种基于事件驱动的网络编程模型,核心思想是:用一个或多个线程监听IO事件,事件就绪后分发给对应的处理器(Handler)执行。

主要组成部分:

  • Reactor(分发器):负责监听事件,调用 epoll/select 等多路复用接口
  • Acceptor:处理新连接
  • Handler:处理具体的读写业务逻辑

常见变体有三种:单Reactor单线程(Redis采用)、单Reactor多线程、主从Reactor多线程(Nginx、Netty采用)。主从模式中,主Reactor只负责接受连接,子Reactor负责处理IO读写,业务逻辑再交给线程池,充分利用多核性能。

4. Reactor 模型底层是如何实现的?

底层依赖操作系统提供的IO多路复用机制,在Linux上主要是 epoll。实现流程如下:

首先创建 epoll 实例,将监听socket注册进去。主循环调用 epoll_wait 阻塞等待,有事件就绪时返回事件列表。根据事件类型(可读/可写/错误)分发到对应的回调函数处理。

epoll 使用红黑树管理注册的fd,用链表存储就绪事件,时间复杂度为O(1),相比 select 的O(n)轮询性能大幅提升。epoll 还支持边缘触发(ET)和水平触发(LT)两种模式,ET模式下只在状态变化时通知一次,需要一次性读完数据,效率更高但编程复杂度也更高。

5. IO多路复用中 epoll 上层有哪些封装?LT 和 ET 的区别?

常见的上层封装库有:

  • libevent:跨平台,封装了 epoll/kqueue/select,提供统一的事件循环接口,被 memcached 等广泛使用
  • libev:比 libevent 更轻量,接口更简洁
  • libuv:Node.js 底层使用,支持异步IO、定时器、线程池等

LT(水平触发)是默认模式,只要缓冲区中还有数据,每次 epoll_wait 都会通知,编程简单不易出错。ET(边缘触发)只在状态从无到有变化时通知一次,要求应用层必须循环读取直到 EAGAIN,否则会漏事件。ET模式减少了系统调用次数,性能更高,Nginx 默认使用 ET 模式。

6. 负载均衡中如何用 Round Robin 实现带权重的任务分配?

普通 Round Robin 轮询每个节点各分配一次请求,无法体现服务器性能差异。带权重的 Round Robin(Weighted Round Robin)有几种实现方式:

最常用的是平滑加权轮询(Nginx采用):每个节点维护一个当前权重,每次选择当前权重最大的节点处理请求,选中后将其当前权重减去总权重之和,其余节点当前权重各加上自身配置权重。这样分配结果更均匀,不会出现连续请求都打到同一节点的情况。

例如节点A权重5、B权重3、C权重2,总权重10,经过10次请求后每个节点恰好被选中5、3、2次,且分布均匀不集中。

7. 操作系统内存淘汰算法有哪些?随机淘汰有什么好处?

常见的页面置换算法:

  • OPT(最优替换):淘汰未来最长时间不会用到的页,理论最优但无法实现
  • LRU(最近最少使用):淘汰最久未访问的页,实际效果好,但精确实现需要维护访问时间戳,开销大
  • LFU(最不经常使用):淘汰访问频率最低的页,容易受历史数据影响
  • Clock(时钟算法):LRU的近似实现,用一个访问位代替时间戳,开销小
  • 随机淘汰:随机选择一个页面淘汰

随机淘汰的好处在于实现极其简单,没有维护链表或时间戳的额外开销,在某些访问模式下(如均匀随机访问)性能不亚于LRU,且不存在缓存污染问题。Redis 的内存淘汰策略中就提供了 allkeys-ran

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

C++八股文全集 文章被收录于专栏

本专栏系统梳理C++技术面试核心考点,涵盖语言基础、面向对象、内存管理、STL容器、模板编程及经典算法。从引用指针、虚函数表、智能指针等底层原理,到继承多态、运算符重载等OOP特性从const、static、inline等关键字辨析,到动态规划、KMP算法、并查集等手写实现。每个知识点以面试答题形式呈现,注重原理阐述而非冗长代码,帮助你快速构建完整知识体系,从容应对面试官提问,顺利拿下offer。

全部评论

相关推荐

发个面经积攒人品。1.  (开场)请做一个简单的自我介绍。2.  (算法题)实现一个时间复杂度最低的排序算法(给定正整数且已知最大值)。3.  (Java基础)Java的基本数据类型有哪些?4.  (Java基础)byte类型的取值范围是多少?5.  (Java基础)int占几个字节?6.  (Java基础)你知道Java的拆箱和装箱吗?7.  (Java基础)拆箱和装箱会带来什么问题?8.  (Java基础)它(指Integer的缓存机制)一定会有拆箱和装箱的开销吗?9.  (Java集合)Java里面的集合类分为哪些类型?10. (Java集合)List里面的ArrayList和LinkedList有什么区别?11. (Java集合)为什么会有这个区别?(指上一个问题中两个List的不同特性)12. (Java集合)ArrayList扩容是怎么扩的?13. (Java集合)为什么(ArrayList)扩容1.5倍?14. (Java集合)HashMap你了解吗?它是什么结构?15. (数据结构)为什么红黑树的查询性能(比链表)好?16. (数据结构)二叉(搜索)树的查询效率一定是O(log n)吗?17. (数据结构)那为什么不做一个完全平衡的(AVL)二叉树?18. (Java集合)HashMap的哈希算法是怎么样的?19. (Java并发)Java里面多线程编程,为了保证线程安全,有哪些技术?20. (Java并发)刚刚说的Atomic是怎么去实现线程安全的?21. (Java集合)HashMap是线程安全的吗?22. (Java集合)有哪些线程安全的Map实现?23. (Java并发)ConcurrentHashMap是怎么去实现(线程安全)的?24. (Android)安卓里面的Handler机制你了解吗?25. (Android)你刚刚提到的epoll机制,它是什么样的(通信机制)?26. (Android)安卓的那个RecycleView你了解过吗?27. (Android)安卓里面进程间通信的方式(有哪些)?28. (Android)你有了解Binder机制吗?29. (框架)看你简历上有提到Retrofit框架,你用过这个框架吗?30. (框架)Retrofit的框架是怎么去实现它接口调用的(机制)?31. (反问前)你那边有问题要问我吗?
查看30道真题和解析
点赞 评论 收藏
分享
评论
点赞
2
分享

创作者周榜

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