ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

百度2018校招核心系统工程师笔试题第三批考点解析与备考指南

百度2018校招核心系统工程师笔试题第三批考点解析与备考指南 前几天在后台收到一个同学的私信问百度2018校招核心系统工程师笔试题第三批到底考什么说自己找了一圈只看到零散的回忆版看不出重点。这个问题其实挺有代表性的核心系统工程师这个岗位的笔试跟普通后端开发笔试差别不小很多人刷了一堆LeetCode还是不知道从哪里下手。我当年整理过这份卷子的复盘笔记也带过几届准备校招的学弟学妹今天就把它展开聊聊。这篇内容围绕笔试题目本身拆解考点、梳理答题思路也聊聊这类岗位背后的能力要求适合准备大厂基础架构、系统研发方向校招的同学以及想往底层走的后端工程师参考。1. 这份笔试题在考什么核心系统工程师的画像1.1 岗位定位决定命题方向核心系统工程师从名字就能看出来不是普通业务开发。干的活是搜索引擎的索引存储、大规模分布式文件系统、消息队列、计算平台这一类底层组件。这些组件有一个共同特点用户每次请求背后都是海量的并发、严格的延迟要求、极高的可用性指标。所以笔试不会只是考你背几句八股文而是要看你能不能处理真实的系统性问题。百度2018校招这批岗位的笔试整体风格比较“硬核”知识点集中在C/Java语言底层、操作系统、计算机网络、分布式系统、数据结构和算法。第三批的难度相比前两批没有明显放水题型更杂覆盖面更广有不少需要“知其所以然”的题。出题人过滤的就是那些简历会写、一问原理就慌的人。我见过不少同学把“核心系统工程师”想象成“高级后端”备考时猛刷Spring、MySQL索引优化这其实跑偏了。核心系统方向更接近基础架构团队写的是被其他团队依赖的组件所以笔试题很少考某个框架怎么用而是反复拷问操作系统、网络协议、内存模型这些底层机制。换句话说业务后端考的是“你会不会用”核心系统考的是“你懂不懂为什么”。1.2 第三批试卷的整体结构与难度分布从试卷结构来看一般是选择题单选多选简答题编程题有时是代码补全这样的组合。选择题部分考得很细比如操作系统的死锁条件、TCP拥塞控制的状态变化、C虚函数表的内存布局多选题尤其容易错。多选和单选不一样漏选、错选都不得分想拿分必须对每个选项都有明确的判断不能靠排除法蒙。简答题偏向设计思路比如“如何设计一个高可用KV存储”“多线程下怎么做任务分发”。这类题没有标准代码但很考验工程思维。编程题则偏向算法系统结合的题比如带约束的并发模拟、海量数据的排序、缓存淘汰策略的设计。因为原卷没有完整公开网上流传的大多是回忆版我这里基于常见考题和考场经验做考点梳理没办法逐字复原每道原题但核心考点覆盖是相对完整的。你们在实际笔试里遇到的具体题目可能措辞不同但考察的知识体系基本就是下面这几块。2. 高频考点拆解从原理到答题套路2.1 语言基础与内存管理C/Java的底层功底核心系统非常吃语言底层功底C问得多的包括虚函数表、内存布局、智能指针、移动语义、new/delete和malloc/free之间的区别。Java则偏向JVM内存分区、GC算法、类加载机制。选择题和简答常问虚函数是怎么实现多态的对象里存的是什么答题不能只背结论要能画内存布局图、说清虚函数表指针存在哪里、对象的构造和析构顺序。我见过太多人把虚函数表说成“对象里存了一张表”其实对象里存的是虚表指针虚表是编译期生成、放在只读数据段的。如果连静态类型和动态类型的区别都说不清那这道题基本就丢了。还有一道很典型的题malloc和new有什么区别很多人的回答是“malloc是C的new是C的new会调用构造函数”这只能拿一半分。更完整的答法是malloc按字节分配、返回void*、失败返回NULL、需要手动指定大小new是运算符会计算类型大小、调用构造函数、失败抛异常而且底层实现通常还是调用malloc或系统调用。再往下钻一点可以说说ptmalloc2的内存池机制、mmap和brk的分界阈值一旦展开到这个深度阅卷人一眼就能看出你是真写过底层代码的。2.2 操作系统与并发锁、进程线程、调度核心系统工程师基本天天跟并发打交道笔试里操作系统考得比较多进程和线程的区别、上下文切换开销在哪、协程的实现原理、互斥锁和自旋锁的适用场景、无锁编程的基本思路。有一道非常经典的多选题哪些操作会导致用户态到内核态的切换系统调用、缺页异常、中断都会而普通的函数调用不会。如果对Linux的syscall和glibc封装理解不够很容易在这道题上失分。多选意味着每一个选项都要判断到位一个拿不准就可能整题丢分。另外一个高频考点是进程间通信管道、消息队列、共享内存、信号量的区别和适用场景。尤其要注意共享内存为什么是性能最高的方式因为它没有内核态拷来拷去的开销但也带来了同步问题。结合这个点再去理解mmap、Redis的AOF重写、Kafka的page cache利用就会自然串起来。准备这部分时建议把《深入理解计算机系统》的异常控制流、并发编程章节精读一遍再搭配Linux性能工具perf、strace看看实际系统调用行为。别只背面经要真的在服务器上跑几段代码去验证。2.3 网络协议与高性能IOTCP、epoll、Reactor搜索、存储这类系统的IO路径就是生命线网络编程题几乎是必考也是最容易拉开差距的。考点集中在TCP三次握手和四次挥手、TIME_WAIT状态、粘包/半包问题、epoll的LT和ET模式、Reactor模型的线程模型。关于epoll常问的是ET模式下为什么需要循环读取直到返回EAGAIN因为边缘触发只在状态变化时通知一次如果不把数据读完剩余数据可能一直等不到下一次通知。这类问题光背八股不够得写过epoll的服务端代码才能答到点子上。可以自己动手写一个简单的echo server用strace观察行为变化把LT和ET的差异实际跑出来。TCP握手还有一个很值得展开的点第三次握手丢了会发生什么服务端会重传SYNACK重传次数由tcp_synack_retries控制同时服务端进入SYN_RECV状态连接队列占用着资源。这个问题的工程意义在于很多高并发场景下的连接建立失败都跟半连接队列溢出有关。我建议复习网络时尽量“带场景”去理解。比如TIME_WAIT是为了保证主动关闭方的最后一个ACK能被对端收到同时也让旧连接的报文在网络中消散所以需要2MSL。理解了这两点就能解释为什么高并发短连接服务会出现大量TIME_WAIT以及如何通过长连接或调整内核参数来缓解。2.4 数据结构与算法笔试不只考验刷题量编程题部分算法题目的难度不会特别离谱一般在LeetCode中等偏上但经常披着“系统题”的外衣。比如让你实现一个支持按时间戳过期和LRU淘汰的缓存表面上是设计题其实考的是哈希表双向链表的组合能力还有的海量数据排序考察外排序和归并的思想而不是直接堆内存排序。LRU是一个很好的例子。面试官不是想看你能不能背出“哈希表加双向链表”而是想看你能否处理几个关键细节get操作要把节点移到链表头部这要求O(1)定位链表需要自己实现不能用std::list再配迭代器否则删除时找不到前驱容量满时要淘汰尾部节点并删除哈希索引并发场景下加锁粒度怎么设计。把这些细节讲清楚代码自然就写出来了。刷题的时候不要把每道题只当题目做多想想它落到真实系统里是什么场景。LRU在Redis、MySQL buffer pool里都在用外排序在海量日志处理里天天遇到带过期时间的缓存是本地缓存组件的标配能力。理解真实场景后写出来的代码层次会不一样。3. 从笔试反推工程师能力模型面试官真正在筛选什么3.1 工程能力与理论基础的比例从这份笔试题可以发现核心系统工程师岗位对“底层原理”的考察超过了“业务堆栈”。同样是校招业务后端可能更看重你对框架的熟练度但核心系统工程师更看重你对操作系统、网络、语言底层机制的掌握。判断标准很简单如果在多线程下让你设计一个数据结构和无锁队列你能不能讲清内存序的问题这背后其实是一个很现实的逻辑核心系统一旦出问题影响的不是某个页面而是全公司的其他服务。所以招聘方宁愿要一个理论基础扎实、动手能力强的候选人也不想要一个只会调框架、出了事连栈都看不懂的人。备战方向因此不能只刷题。我建议至少把《操作系统概念》或《深入理解计算机系统》、TCP/IP协议栈、C对象模型这几块知识过一遍形成体系而不是零散记考点。学的时候多问自己一个问题“这个机制在真实系统里解决什么问题”带着问题学效率会高很多。3.2 系统设计题的隐含评分点简答题或设计题没有标准答案但阅卷人心里有评分点有没有考虑并发、数据一致性、高可用、扩展性、监控、降级。比如设计一个高可用KV存储光说“用哈希分片”是不够的要能落到分片策略怎么选、数据冗余几副本、主从切换怎么做、脑裂怎么防、持久化用Raft还是异步复制、异常情况下的降级策略。一个实用的答题框架功能需求 - 非功能需求延迟/可用性/一致性 - 数据模型 - 架构方案 - 关键路径时序 - 容错与降级 - 监控运维。按这个顺序写哪怕技术选型不是最优逻辑也是完整的。这里特别想说一下“一致性”这个点。很多同学一上来就写“强一致”但强一致是有代价的Raft多数派写会把写放大成三倍以上。如果业务允许最终一致为什么不选设计题里能不能根据场景主动做取舍是最能拉开档次的地方。比如一个读多写少的配置中心可以读走本地缓存加版本号校验一个交易系统读和写都必须强一致。把取舍讲清楚比堆一堆“高可用、高性能”的形容词有用得多。3.3 如何针对这份试卷做备战时间有限的情况下优先级这样排算法题不能丢这是硬门槛操作系统和网络的高频原理题要能默写系统设计题至少完整写三到五篇不要只在脑子里过语言底层准备好一两个能讲深的话题比如C虚函数和智能指针。另外推荐去看看百度之星这类编程竞赛的题目找找手感核心系统工程师笔试的编程题味道跟竞赛题有些接近但更偏工程场景。做竞赛题能训练你在有限时间内快速建模、快速实现的能力这在笔试现场非常关键。还有一个容易被忽略的准备项限时模拟。建议考前一周找完整的两小时按真实考试状态做一套题手机收起来、禁止查资料、到点就停。很多人平时复习感觉良好一进考场就崩就是因为没有提前适应考试的节奏和压力。4. 实战演练典型考题的思路拆解含代码思路4.1 一个并发编程题的完整解题过程假设笔试里出现类似题目设计一个线程安全的计数器要求高并发下性能尽量好。初级答法是用synchronized或std::mutex包一下高级答法要考虑原子变量、CAS、分段计数、伪共享问题。如果要求极端性能还可以按线程ID做分段统计读取时再汇总。我写一版分段计数的C示例#include atomic #include vector class ShardedCounter { public: explicit ShardedCounter(int shard_count) : shards_(shard_count) {} void Add(int tid, int delta) { shards_[tid % shards_.size()].fetch_add(delta, std::memory_order_relaxed); } long long Get() const { long long sum 0; for (auto s : shards_) { sum s.load(std::memory_order_relaxed); } return sum; } private: std::vectorstd::atomiclong long shards_; };分段计数避免了全局竞争但要注意CPU缓存行填充的问题多个原子变量挨在一起可能落到同一个缓存行出现伪共享。填充到64字节再对齐是常见的优化手段可以用alignas(64)修饰元素或者把数组长度乘以8并错开索引来模拟。答到这个深度笔试基本就稳了。另一个能在答案里加分的点是内存序。计数场景不需要依赖其他数据用memory_order_relaxed就够因为不要求全序同步。但如果你要把计数和某个标志位一起用那就要考虑release/acquire语义。能把“为什么用relaxed”说清楚比单纯堆一堆高级词汇强得多。4.2 一个网络编程题的核心实现要点比如让你用非阻塞IO和epoll实现一个简单的HTTP server要求支持多个并发连接。核心思路是创建监听socket设置为非阻塞加入epoll有读事件时循环读取并解析请求解析完成后组织HTTP响应并写回。ET模式下读要循环到EAGAIN写要处理缓冲区满的情况用事件驱动的方式管理每个连接的状态。还有一个常被忽略的点HTTP请求头可能分多次到达需要自己维护一个应用层缓冲区不能假设一次recv就能拿到完整请求。很多初学者栽在这里他们读完第一段数据就直接解析结果请求不完整解析失败连接也被错误关闭。正确做法是先存缓冲区检查是否有\r\n\r\n有再解析。下面是一段核心循环的骨架展示事件处理结构// epoll_wait返回后对每个就绪fd // 1. 如果是listen fdaccept所有新连接设为非阻塞加入epoll // 2. 如果是连接fd且触发EPOLLIN循环recv到EAGAIN // 把数据追加到per-conn buffer尝试解析HTTP请求 // 3. 解析到完整请求后生成响应先尝试直接write // 没写完就在epoll里注册EPOLLOUT等可写事件再继续 // 4. 如果是EPOLLOUT事件写剩余数据写完移除EPOLLOUT把上述流程按模块拆开先写网络层再写协议解析层最后加线程池处理业务逻辑整个题的完成度会高很多。面试官看到你能把“连接状态机”“缓冲区管理”“事件处理”分开思考就会认可你的工程能力。4.3 一道系统设计题的答题框架以“设计一个短网址服务”为例虽然它不是存储系统但能完整考察系统设计功底。先列非功能需求高并发读、写少读多、可用性高、延迟低。存储选型可以用分布式KV或关系型数据库关键是生成短码的策略哈希后取前几位、自增ID转换62进制、发号器预分配区间。我最推荐的是“发号器预分配”方案。维护一个全局发号器可以用数据库行锁或Redis INCR每台服务启动时批量申请一段ID区间比如从10000到20000服务本地维护当前ID转成62进制字符串作为短码。这种方式的好处是生成短码不依赖每次远程调用发号器压力小而且天然支持水平扩展。相比“哈希后查重”的做法不会出现碰撞重试的麻烦。短网址服务的读写路径也要设计好。读路径先查本地缓存缓存是短码到长网址的映射命中直接返回没命中就回源KV存储然后回填缓存。回源时要防止缓存击穿常用的办法是单飞singleflight大量相同请求只允许一个打到后端其余等待结果。写路径用户提交长网址后通过发号器拿ID生成短码写入KV存储。答这种题画清每个环节的时序和容错方式比单纯堆技术名词有用。比如发号器挂了怎么办本地区间用完了怎么办缓存节点挂了会不会影响读把这些异常路径都列出来才算一个完整的设计。5. 常见问题与避坑技巧5.1 关于时间分配与做题顺序笔试时间一般120分钟选择题简答题编程题全做完很紧张。我的建议是先花5分钟扫一眼所有题目把编程题按难度排个序选择题卡住不要超过2分钟先标记跳过去编程题优先做自己最有把握的拿到基础分再回头啃难题。很多同学挂在“第一道编程题写了太久后面的题没时间看”这是最亏的。一套卷子的编程题往往第一道不是最简单的如果你按顺序硬写很可能被卡住半小时后面明明会的题也没时间做。先做有把握的题是应试的基本策略不是投机取巧。还有一个小技巧简答题如果一时没思路先写下能想到的关键词和组件再逐步组织成完整答案。哪怕最后不够完善至少能让阅卷人看到你的分析路径拿部分分。空着不写等于放弃写错了方向也比交白卷强。5.2 笔试环境与编程题提交流程的坑在线笔试环境通常不允许本机IDE调试或者只能在线编辑。写完代码一定要自己复查边界条件输入为空、数据量很大、整型溢出、指针为空、并发的竞态。有些系统只跑少量用例就能看到结果但千万不要拿“过了示例”当“能过全部用例”。隐藏用例才是大头。我见过有人因为函数签名没按题目的要求写导致编译失败整题零分。提交前一定要检查函数名对不对、参数类型对不对、返回值类型对不对、有没有多写或漏写头文件。如果题目要求从标准输入读数据输出格式也要严格匹配多个空格、少个换行在线判题系统都可能判你错。还有提交前留出时间把代码格式、注释整理一下有些卷子是人工盯代码的一份整洁的代码印象分会好很多。变量命名别用a1、a2、tmp这种稍微起个有意义的名字哪怕笔试判题不看重人工批改或后续面试官翻看你代码时印象完全不同。5.3 备考中的一些实用建议不要只背面经要能默写核心代码比如手写线程池、手写LRU、手写epoll echo server。把操作系统、网络、C、算法四块做成自己的知识树遇到不会的题先定位考的是哪棵树的知识。系统设计题一定要动手写文档不要只在脑中推演。最后一周建议做限时模拟就是按考试状态完整做一套题锻炼时间感觉。关于手写代码我想多说一句。很多人觉得“我会看代码会调API就够了”但笔试现场没有自动补全、没有IDE提示一切都要靠手写。你要是平时没动手写过写出来的代码很可能连编译都过不了。建议每天抽半小时在白板上写一小段核心代码写完之后再和正确实现对比找到自己容易漏掉的边界。如果时间充裕还可以去看看做核心系统相关的开源项目比如高性能事件库、分布式存储的早期版本把源码里一些关键数据结构读明白。这不一定直接对应到笔试题目但能帮你建立“系统思维”对简答题和设计题的提升很明显。说到底这些经验不只是为了这一场笔试。进了公司以后面对线上故障排查、性能优化你会发现同一个知识体系一直在用。笔试只是把“你平时积累得怎么样”这件事暴露出来而已。这些年带过的学生里能进核心系统工程师岗位的几乎都有一个共同点不是记性最好、刷题最多的而是愿意把每个知识点往底层钻一点的年轻人。比如同样是“缓存”有人只记住Redis怎么用有人会去翻RDB和AOF的实现区别同样是“锁”有人只会背悲观锁乐观锁有人能画出对象头里的锁状态变化。笔试筛掉的其实是那些只会表面功夫的人。如果你也打算往这个方向走我的建议很朴素沉下心把原理啃透再配足量练习不用慌。这份笔试就是一次体检平时练到位了它自然会给你一个高分。
返回列表