滴滴 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。

全部评论

相关推荐

04-04 14:34
门头沟学院 Java
本人双九硕,结合多份真实面经 + 大量候选人反馈,整理出字节跳动暑期实习面试避坑指南,全是从真实经历中总结的血泪教训,帮大家避开雷区、高效备战!先明确核心前提:字节暑期实习面试,核心考察项目经验、代码能力和实际场景应用,无过多八股,侧重真实能力与逻辑思维,以下是重点避坑点 + 注意事项,全程贴合真实面试场景。一、字节面试核心避坑点(重中之重!)1.面评有 “记忆点”,且永久留存!字节的面试评分的是永久记录,所有面试官都能看到你的历史面评,哪怕换岗位、换账号投递,系统也会自动合并记录,一旦有负面评价(比如态度差、代码不过关),后续再投很难被捞。2.代码考察严格,避坑别踩雷!手撕代码是重点,高频考察无重复字符最长子串、第 K 大、LRU 缓存、反转链表这 4 道题,多集中在一面;且会全程监控切屏(飞书可直接检测),千万别偷偷查答案,一旦被发现,直接挂掉。3.业务岗与研究岗差异大,别投错!业务岗侧重落地执行,代码要求更高;研究岗侧重算法深度,对论文和项目的考察更细致,选错方向会大幅降低通过率,投递前一定要确认岗位类型。4.别盲目 “试错”,拒绝会影响后续!若拿到字节 offer 后又拒绝,短期内再投会被优先过滤;若只是想练手,建议先不投字节,避免留下 “拒绝记录”,影响后续投递。5.基础不牢,再努力也白搭!无论是业务岗还是研究岗,都会重点考察基础算法和代码功底,哪怕项目经验丰富,基础题答不上来,也很难通过二面。二、高频踩坑场景(附避坑技巧)踩坑 1:盲目投递,未区分岗位类型很多同学分不清字节业务岗和研究岗的区别,盲目投递后发现方向不匹配,面试时被问懵,白白浪费机会。建议投递前先确认岗位侧重(是侧重落地还是研究)。踩坑 2:代码准备不充分,临时抱佛脚字节手撕代码难度高,且多考察 ACM 模式,若未提前刷 Hot100、未熟悉 ACM 答题规范,大概率会在一面挂掉,建议提前针对性练习。踩坑 3:面试时与面试官争执遇到观点分歧时,无需强行辩解,客观说明自己的思路即可,过度争执会降低面试官好感,直接影响面试结果。踩坑 4:忽视 HR 面细节别以为技术面过了就稳了,HR 面会重点了解你的稳定性和求职诚意,若表现出 “只是练手”“不想长期发展”,很容易被刷。三、备考 & 投递建议1.代码准备:重点刷 Hot100 高频题,尤其是字节常考的 4 道手撕题,练熟 ACM 答题格式,避免因格式问题丢分;2.项目 & 论文:提前梳理自己的项目亮点和论文核心,面试时重点突出自己的贡献,避免泛泛而谈;3.投递节奏:字节暑期流程较快,建议集中投递,避免分散精力,投递后及时关注进度,避免错过面试通知;4.心态调整:面试时遇到不会的问题,坦诚说明即可,不要瞎编,面试官更看重你的学习态度和逻辑思维,而非完美答案。四、补充提醒字节暑期实习的核心门槛的是 “能力匹配 + 稳定性”,若拿到 offer 后又拒绝,后续再投难度会大幅增加;另外,字节不喜欢 “盲目试错” 的候选人,投递前一定要明确自己的职业规划,避免浪费双方时间。后续会持续更新字节高频面试题、手撕代码技巧,助力大家顺利拿下字节暑期 offer!
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

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