2019年校招季我投了百度核心系统工程师这个岗位,拿到第一批笔试的时候说实话有点懵。这套卷子和市面上常见的后端开发笔试题完全不是一个画风:没有框架八股,没有微服务调优,扑面而来的全是操作系统、网络协议、分布式一致性,甚至还有不少Linux底层和性能分析的内容。今天这篇复盘,我就给还在准备系统工程师、基础架构方向校招的学弟学妹们拆一拆,这套题到底在考什么、每种题型的核心套路是什么,以及我在实际做题过程中踩过的坑和总结出来的复习方法。
先说明一下,具体题目细节属于回忆整理,我不会逐字复述原卷,而是把这批题里最典型、最有区分度的考点拿出来做深度拆解。你只要照着这个方向准备,即使后面题目形式变了,核心得分点也不会跑偏。
1. 试卷整体结构与命题思路拆解
1.1 核心系统工程师岗位到底在招什么人
百度“核心系统”这几个字,对应的是底层基础设施方向,包括大规模分布式存储、消息队列、统一调度、内核网络优化这些方向。笔试筛选的不是“会写CRUD的人”,而是能吃透底层原理、能扛住极端场景的人。
我当时理解到这一点之后,整张卷子就越做越顺了。比如操作系统的题不会只问你“什么是死锁”,而是给你一个多线程程序,让你分析锁的持有顺序、条件变量使用的对错。网络题不会只让你背三次握手,而是给出tcpdump抓包结果,让你判断连接为什么卡在SYN_RECV。所以准备这批题,第一要务是把“原理”和“现象”绑定起来,不能停留在名词解释层面。
岗位本身决定了题目范围。核心系统工程师平时打交道最多的是Linux服务器、内核参数、网络协议栈、分布式组件,以及海量数据下的性能问题。所以操作系统、计算机网络、数据结构算法、分布式系统这四块,基本占了整张卷子80%以上的分数。剩下的编程题,也往往不是纯算法竞赛题,而是带工程背景的题目,比如多线程日志、海量数据去重、缓存淘汰。这意味着你光会刷LeetCode还不够,还得有把算法放到真实场景里落地的能力。
1.2 第一批笔试题型与考点分布
从题型来看,第一批笔试主要分三块:客观题、简答题、编程题。客观题包含单选和多选,覆盖面很广,但每题分值不高;简答题通常要求写方案或分析原因,是拉开差距的地方;编程题一般两道左右,一道偏算法,一道偏系统设计或并发处理。
我根据自己的回忆和同期同学的反馈,整理了一张考点分布表,大家可以对照着查漏补缺:
| 知识模块 | 典型考点 | 出现形式 | 难度 |
|---|---|---|---|
| 操作系统 | 进程线程、内存分页、页面置换、死锁、零拷贝、IO模型 | 选择、简答 | 中高 |
| 计算机网络 | TCP/UDP、三次握手四次挥手、TIME_WAIT、滑动窗口、拥塞控制 | 选择、简答 | 中高 |
| 数据结构算法 | 链表、LRU、TopK、哈希、布隆过滤器、二叉树遍历 | 选择、编程 | 中 |
| 分布式系统 | CAP、Raft/Paxos、分布式锁、一致性哈希、缓存穿透 | 选择、简答 | 高 |
| 语言底层 | C++内存、智能指针、Go goroutine、并发安全 | 选择、编程 | 中 |
| Linux/工具 | 常用命令、软硬链接、进程通信、系统调用 | 选择 | 低中 |
注意,这个分布不是官方的,而是我根据考生回忆拼出来的。但它大致反映了核心系统工程师岗位的考察逻辑:宁可多考基础,也不考花哨框架。
另外,这批题有一个很明显的特征:客观题喜欢在“看似简单的结论”上做文章。比如问你TCP主动关闭连接的一方会进入什么状态,答案可能不是CLOSE_WAIT而是TIME_WAIT。如果你只是背过“四次挥手”但没理解谁主动谁被动,很容易被绕进去。我建议做这类题的时候,尽量把连接状态图、内存管理图都亲手画一遍,不要只靠脑子记。
1.3 和普通后端开发笔试的最大区别
我之前也刷过不少后端开发岗位的笔试题,对比之下,核心系统工程师的题目有两点明显不同。
第一,不考Web框架,不考业务设计。普通后端笔试可能会问“如何设计一个秒杀系统”“JVM调优参数怎么设置”,但这套卷子基本不碰这些。它更关注的是消息队列的可靠性、文件系统读写延迟、多线程并发下的性能瓶颈这类问题。因为核心系统工程师写的代码,往往要跑在成千上万台服务器上,一个锁粒度没控制好,可能就是全集群的性能事故。
第二,对“量级”极度敏感。普通开发题可能让你设计一个日活百万的系统,核心系统题动辄就是百亿级数据、千台机器。我记得有一道题涉及海量URL访问频次的统计,如果没做过内存估算,很容易写出一个直接OOM的方案。后面我会专门讲这种题的完整计算过程,这是很多人复习时最容易忽略的部分。
如果你已经决定走系统工程师方向,建议把复习重心从“应用层解题”切换到“原理层理解”。在读源码、看面经、做笔试复盘的时候,多问自己一句:为什么底层要这样设计?换一个场景会有什么问题?这套卷子筛的就是这种思维方式。
2. 高频考点深度拆解:原理、公式与避坑
2.1 操作系统:页面置换、零拷贝与IO模型
操作系统在整张卷子里出现频率极高,而且考察方式非常具体。我先说页面置换算法,因为这是选择题里的常客,也是很多人容易算错的点。
经典的引用串训练题是:7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1,物理块数3。分别用FIFO、LRU、OPT去算缺页次数,FIFO会缺15次,LRU缺12次,OPT缺9次。这个结果本身不重要,重要的是你要知道为什么LRU比FIFO更接近最优。FIFO只看进入内存的先后,完全不考虑“近期是否被访问”;LRU则假设“最近用过的数据,未来大概率还会用”,所以每次淘汰最久没被访问的页。实际系统里不可能精确记录所有页的访问时间,所以才会出现Clock算法这种近似实现,用访问位来模拟。
笔试里如果给的是带访问位、修改位的Clock算法,不要只数缺页,还要注意置换指针的移动规则。我记得当时有同学就因为没搞清“第一轮只找访问位为0的页,找不到才轮转”这个规则,把整个算错了。
另一个我印象很深的知识点是零拷贝。题目可能会问:从一个文件读取数据再通过socket发送,标准read+write做了几次拷贝和几次系统调用?答案是:四次上下文切换、四次数据拷贝(两次DMA,两次CPU拷贝)。然后会问mmap、sendfile分别减少了哪一次拷贝。
这里我建议用一个类比来理解:read+write就像你要把一摞文件从A办公室送到B办公室,中间必须自己先走到文件柜,拿出来放到自己桌上,再走到B办公室放下去。mmap相当于文件柜和你的办公桌共用一块玻璃板,你不需要自己誊抄,直接指给B看。sendfile更极端,让专门跑腿的人(DMA)直接帮你搬,你只需要下个指令。理解了数据流,选择题基本不会错。
IO模型也是必考项。阻塞IO、非阻塞IO、IO多路复用、信号驱动IO、异步IO这五种要能讲清楚区别,尤其是同步和异步的分界线。epoll在笔试里的高频追问是:水平触发LT和边缘触发ET有什么区别?ET模式为什么效率高但容易漏事件?
我当时是这么记的:LT模式下,只要缓冲区还有数据,epoll_wait就反复通知你;ET模式下,只有状态发生变化时才通知一次。所以ET模式要求你把数据一次性读完,否则剩下的数据可能要等下次新事件到来才会再被通知。实际工程里,ET模式更接近“有消息就来一次”的高效模型,但处理复杂度更高。笔试问到的时候,把“事件驱动、边缘触发、循环read直到EAGAIN”这几个关键词打出来,基本就是标准答案。
2.2 网络协议:TCP状态机与握手细节
网络部分的难度比校招平均水平高一截。这里不是说题目多偏,而是细节抠得很深。三次握手、四次挥手这种基础题不多说,我重点讲几个容易丢分的变体。
一个很典型的题:客户端主动关闭连接,过程是客户端先发FIN,服务端回ACK,服务端再发FIN,客户端回ACK。此时客户端进入TIME_WAIT状态,要等待2MSL才能完全关闭。题目可能会问:为什么TIME_WAIT要等2MSL?标准答案有两个层面:一是保证最后的ACK能到达服务端,万一ACK丢了,服务端会重发FIN,客户端还能再回一次;二是让旧连接产生的所有报文段在网络中自然消失,防止污染新连接。
另一个常见坑是SYN队列和Accept队列。服务端收到SYN后,先放入半连接队列,完成三次握手后移入全连接队列,然后由accept()取出。如果半连接队列满了,新连接会被丢弃或忽略,表现为客户端一直处于SYN_SENT,服务端看不到连接建立。题目如果给你一个线上故障:大量连接处于SYN_RECV,CPU不高但连接建不起来,大概率是SYN队列被打满或者backlog设置太小。答这类题要抓住“队列长度、内核参数、攻击/并发”几个关键词。
TCP相关还有一个必须会的计算题:在已知带宽和RTT的情况下估算最大吞吐量。公式很简单:吞吐量上限 ≈ 窗口大小 / RTT。比如窗口是64KB,RTT是10ms,那最大吞吐量就是 64KB / 10ms = 6.4MB/s。如果题目再升级,问你带宽100Mbps、RTT 20ms时窗口至少多大才能打满带宽,那就要反过来算:100Mbps × 0.02s = 2Mbit = 250KB。这个换算关系在TCP性能调优里经常出现,笔试也很喜欢出,因为能同时考察你对单位换算和TCP滑动窗口的理解。
另外,我看到不少人在TCP拥塞控制上只背了“慢启动、拥塞避免、快重传、快恢复”几个名字,但遇到“什么时候进入拥塞避免、ssthresh怎么变”就懵了。建议把拥塞窗口随RTT变化的曲线画一遍,把“慢启动阈值减半”“窗口置为1”这些操作对应到具体流程里。如果时间允许,再看一下CUBIC和BBR的不同,这已经属于加分的深度了。
2.3 数据结构与算法:TopK、LRU与布隆过滤器
算法选择题难度不大,但编程题往往和“海量数据”绑定。我先把三个最高频的考点讲透。
第一个是TopK问题。给你100亿个查询串,统计出现频次最高的100个。直接的排序肯定不行,100亿个字符串本身就是几十TB的规模,单机内存装不下。正确思路是分而治之:先哈希取模分片到多台机器或多个文件,每个分片内用哈希表统计频次,再用大小为K的小顶堆找到该分片的前K个。最后把所有分片的K个候选聚合,再做一次TopK。这里面有几个关键点:哈希分片要保证同一个key一定进同一个分片,所以用hash(key) % N;每个分片只要保留前K个,不用存全量;小顶堆的堆顶是当前最小,只有比堆顶大的元素才替换入堆。
第二个是LRU缓存淘汰。这个属于“必背级”手写题。核心结构是哈希表加双向链表。哈希表负责O(1)查找,双向链表负责O(1)删除和移动。每次get,把节点移到链表头部;每次put,如果容量满了,先删除链表尾部节点,再插入新节点到头部。写代码的时候最容易犯的错是:删除节点时忘记同步更新哈希表,或者移动节点时指针指错。我建议在本地把这题练到五分钟内无脑默写出来,不只因为它是笔试高频题,更因为面试几乎必问。
第三个是布隆过滤器。它用多个哈希函数把key映射到一个位图上。判断“不存在”是100%准确的,但判断“存在”可能有误判。公式需要会算:位数组长度m和哈希函数个数k,在元素量n和目标误判率p下的关系是:
- 位数组长度:m ≈ - n × ln(p) / (ln2)^2
- 哈希函数个数:k ≈ (m / n) × ln2
假设你有1000万个key,希望误判率控制在1%,那m大约为95850583 bit,也就是接近12MB,k取7个左右。这个结果反直觉:1000万个key只要12MB就能挡住绝大多数无效查询。笔试如果让你设计一个缓存穿透方案,布隆过滤器是标准答案之一,答的时候最好把误判率为什么存在、怎么权衡空间和准确率讲清楚。
2.4 分布式系统:CAP、Raft与分布式锁
分布式系统模块是这套卷子里区分度最高的部分,也是核心系统工程师日常工作中避不开的知识。先说CAP,这里最大的坑是“C、A、P三选二”这种粗浅理解。CAP里的C是一致性,A是可用性,P是分区容错性。在网络分区发生时,你只能在一致性和可用性之间取舍,不允许既保证所有节点数据一致、又保证所有节点都能继续服务。分区不是可选项,而是必然发生的情况,所以设计系统时真正做的是“在P存在的前提下选择C还是A”。
Raft算法也是高频考点。它比Paxos容易理解,但笔试问到选举过程时依然有人答不全。核心流程:所有节点初始是Follower,如果超过选举超时时间没收到Leader心跳,就变成Candidate,给自己投票并请求其他节点投票。获得超过半数投票的节点成为Leader。之后Leader负责接收客户端请求、复制日志到Follower、在大多数节点写入成功后提交。这里要注意一个细节:任期term是递增的,投票只能在同一个任期里进行;日志复制要求方向只能是Leader到Follower,不能反过来。能把这些讲清楚,Raft的基础题基本稳了。
分布式锁也是必考题。它考察的不只是Redis的SET key value NX EX,更关键的是锁的“安全性”。我会从三个方案递进分析:
- Redis SETNX实现:简单易用,但要注意不能只SETNX不设过期时间,否则持有锁的进程挂掉就死锁了。设过期时间后又要注意业务执行时间超过锁超时时间的问题,可能有多个进程同时进入临界区。
- Redlock红锁:向多个独立Redis实例申请锁,超过半数成功才算拿到。它能降低单点风险,但依然不是绝对安全的分布式锁,因为GC停顿可能让锁过期。
- ZooKeeper临时顺序节点:创建临时顺序节点,只有序号最小的节点能拿到锁,其他节点监听前一个节点。因为临时节点在会话断开时会自动删除,所以能避免“持有者挂掉不释放”的问题。
笔试如果让你比较分布式锁方案,不要只说某个方案好。要把“可靠性、性能、实现复杂度、应用场景”都摆出来,展示你是在做工程选型,而不是在背书。
2.5 语言底层:C++内存管理与Go调度模型
核心系统工程师的主要开发语言基本是C++和Go,所以笔试也会考察语言底层能力。
C++部分最常看到的是内存管理和智能指针。malloc和new的区别必须能说清楚:malloc只分配原始内存,不调用构造函数;new会分配内存并调用构造函数。free和delete对应也要匹配,混用是未定义行为。然后就是shared_ptr、unique_ptr、weak_ptr的区别。记住一句话:unique_ptr独占所有权,移动语义;shared_ptr引用计数,可以有多个所有者;weak_ptr不增加引用计数,用来打破循环引用。选择题如果问循环引用怎么解决,答案往往是weak_ptr。
还有一个隐蔽考点是“内存泄漏”。笔试里可能会给一段代码,让你找泄漏点。常见的套路是:new了对象但没delete,或者shared_ptr相互引用导致计数永远不为0,或者异常路径上忘记释放资源。答这类题,最好顺手写出用RAII解决的方式,这样即使选择题错了,简答也能拿分。
Go部分主要考goroutine和channel。GPM模型是必讲内容:G是goroutine,P是逻辑处理器,M是操作系统线程。goroutine不是线程,而是运行在线程上的轻量级协程,初始栈只有几KB,可以通过grow stack动态扩容。笔试里如果问“十万个goroutine会不会把内存打爆”,基于2KB初始栈,十万个大概占几百MB,如果是百万级就千万小心。实际更准确的说法是Go的groutine溢出栈和复用机制让它比线程轻量得多,但也不是无限制创建。
并发安全方面,sync.Mutex、sync.RWMutex、atomic、channel要能区分使用场景。数据竞争检测也是常考概念,Go的go run -race可以在运行时检测数据竞争,笔试可能以填空或选择形式出现,问你用什么工具检测。
3. 典型大题实操思路:从读题到答完
3.1 编程题套路:多线程并发日志收集
第一批笔试里有一类编程题很贴近工程:模拟多线程环境下多个worker产生日志,由一个collector统一写入文件,要求不能丢日志、不能乱序,并且性能尽量高。
看到这种题,先别急着写代码,拆解需求的时间一定要留够。主要考察三件事:数据怎么从worker传到collector,锁怎么加才能保证线程安全,文件IO怎么优化才能减少磁盘写入次数。
最简单的方案是每个worker持有一个锁,写日志时直接互斥。这个方案正确,但性能很差,因为所有worker争一把锁,高并发下锁竞争严重。更好的方案是每个worker用一个无锁队列,collector统一从队列里取数据批量写入。如果考试时间紧张,可以退一步写成“加锁+批量缓冲”,关键要体现出你意识到了锁粒度的性能问题。
文件IO部分,尽量不要每条日志都调用一次write,而是攒一批再写。可以设置一个buffer,比如4KB或8KB,buffer满了或者定时刷新才写入磁盘。选择小批量还是大批量又是个权衡:批量太小,系统调用频繁;批量太大,掉电丢数据的风险高。这种地方没有绝对正确的答案,但要让阅卷人看到你在“性能”和“可靠性”之间做了取舍。
我当时写这类题的固定套路是:先写清数据结构,再画关键流程,最后补充互斥逻辑。如果时间不够,代码可能写不完全,但设计思路写出来也能拿一半分。
3.2 海量数据TopK题目的完整计算过程
海量数据题最怕的不是算法不熟,而是数字算不过来。我给你完整演示一道典型题的计算过程,这类题非常容易在第一批笔试中出现。
题目场景大概是:有100亿条URL访问记录,每条URL平均长度64字节,统计被访问次数最多的100个URL。你的机器内存只有4GB,单机处理。
第一步,算原始数据量:100亿 × 64字节 = 6400亿字节 = 640GB。单机内存4GB,肯定存不下,所以必须先分片。
第二步,设计分片数量。如果分成1000个小文件,每个文件约640MB,已经超过内存但还可以通过流式处理。如果分成2000个小文件,每个约320MB,可以分批读入。这里分片数量不是越大越好,文件太多会带来大量随机IO,文件太少又装不进内存。我建议宁多勿少,因为小文件读进内存做哈希统计时,哈希表本身也有额外内存开销,一个URL key存80字节左右,320MB文件里可能就有几百万条记录,哈希表会膨胀到远超文件本身。
第三步,每个小文件用哈希表统计词频,再用大小100的小顶堆保留该文件的Top100。这个阶段每个文件只需要输出100个候选,所以最终候选只有 2000×100 = 20万个,聚合排序完全没问题。
最后一步,把20万个候选再统计一次,得到全局Top100。这道题答到这里,已经把“哈希分片、小顶堆、内存估算、IO模型”全部串起来了,比只写“用MapReduce”要实在得多。
3.3 系统设计题的答题框架:以短URL为例
简答题里比较常见的是“给一个场景,让你给出系统设计方案”。我当时遇到的是短URL方向,但换成长链接、消息队列、Redis缓存也一样。你只要掌握一套固定的答题框架,就能应对大部分设计题。
我的框架是四步走:需求分析、容量估算、方案选型、细节补充。
需求分析要先搞清楚量级。假设每天新增1亿个短URL,每个短URL平均被访问10次,那3年累计数据量就是1000多亿条。存储上必须分库分表,缓存层至少需要支撑每秒约10万次读请求。
容量估算是很多人的薄弱点。1亿个短URL,每个映射关系存原URL和一个7位短码,大概按128字节算,一天1.28GB,3年约1.4TB。如果只用单机MySQL,早就爆了,所以要用分布式KV或分库分表。缓存方面,如果每天访问量为10亿次,缓存命中率做到95%,那Redis集群每秒要扛大约5000次写和10万次读,需要评估单个Redis实例的能力后设计集群规模。
方案选型是拿分重点。生成短码可以采用发号器方案:用数据库自增ID或雪花算法生成全局唯一ID,再转成62进制短码。优点是无冲突、查询快;缺点是发号器本身可能成为性能瓶颈。另一个方案是直接对原URL做哈希,比如MD5取前7位,优点是去中心化,但存在哈希冲突,需要碰撞检测和重试。
最后在细节补充里答题,把缓存穿透、永久重定向、恶意请求防护这些点提一遍,会显得考虑更周全。如果笔试时间紧张,至少把容量估算和方案选型写出来,已经能打败很多人了。
3.4 手写LRU Cache:边界条件才是得分点
LRU Cache是笔试编程题出现频率最高的一道,没有之一。我不光说原理,直接给一个能跑的参考实现,然后讲边界条件。
class Node: def __init__(self, key=0, value=0): self.key = key self.value = value self.prev = None self.next = None class LRUCache: def __init__(self, capacity: int): self.capacity = capacity self.cache = {} self.head = Node() self.tail = Node() self.head.next = self.tail self.tail.prev = self.head def _remove(self, node): node.prev.next = node.next node.next.prev = node.prev def _add_to_head(self, node): node.prev = self.head node.next = self.head.next self.head.next.prev = node self.head.next = node def get(self, key: int) -> int: if key not in self.cache: return -1 node = self.cache[key] self._remove(node) self._add_to_head(node) return node.value def put(self, key: int, value: int) -> None: if key in self.cache: node = self.cache[key] node.value = value self._remove(node) self._add_to_head(node) return if len(self.cache) >= self.capacity: lru_node = self.tail.prev self._remove(lru_node) del self.cache[lru_node.key] new_node = Node(key, value) self.cache[key] = new_node self._add_to_head(new_node)这段代码里最容易出细节分的地方有三个。
第一个是get操作时,如果key存在,除了返回值,还要把对应节点移到链表头部。这是LRU的核心语义:最近被访问过,就不能被优先淘汰。漏写这一步,整个数据结构就退化成了普通缓存。
第二个是put已存在的key时,要先更新value,再移动到头部。顺序不能反,如果先移动再更新,代码逻辑也能跑,但读起来不清晰,而且容易在复杂版本里引入bug。
第三个是容量满时,要先删除尾部节点,再从哈希表里删除对应key,然后插入新节点。删除链表节点和删除哈希表条目必须成对出现,漏一个就会造成内存泄漏或哈希表脏数据。建议在本地多写几遍,把链表操作的四个指针连接练到肌肉记忆。
4. 时间分配、常见失误与备战建议
4.1 三个小时笔试的时间分配策略
这套卷子整体题量不小,我记得做完时候大概还剩10分钟检查。时间分配我建议按下面的节奏来,已经经过自己和多位同届同学的验证:
- 前40分钟:处理选择题和判断题。这类题要不要纠结?我的经验是,超过2分钟还没思路的题先标记跳过,后面如果有时间再回头。因为选择题单位时间得分效率不高,不值得为一道题影响大局。
- 接下来50分钟:做简答题。简答题重点是框架清晰,每个小问尽量写2到4个要点。如果一道简答10分钟内没写完,果断收尾进下一题,宁可少写细节也不能空题。
- 然后60分钟:做编程题。先花10分钟读题和设计数据结构,再开始写代码。最好先写能跑通的基本版本,再去优化边界和性能,避免一上来追求完美导致最后连基础版都没写完。
- 最后30分钟:检查。重点看三个地方:有没有漏题、编码格式对不对、有没有明显语法错误或数组越界。
这套时间分配最大的价值在于:它保证了你不会在某个难题上耗到最后一题空着。校招笔试的淘汰逻辑是“基础题不能错,难题尽量拿部分分”,不是“必须AC全部题目”。
4.2 最容易丢分的几个低级错误
先列一个我亲眼见过以及自己踩过的错误清单,每一条都是用分数换来的。
- 多选题漏选或错选。选择题是多选时,不确定的选项宁少选不多选,因为少选还可能得部分分,错选直接0分。
- 不写单位。凡涉及容量、带宽、内存的题目,必须写KB、MB、Mbps、ms。漏掉单位,哪怕计算过程全对,也可能因为表达不严谨扣分。
- 简答只有结论没有推导。比如问“为什么不能直接三选二”,只写“CAP不可兼得”完全没用,要把分区容错的前提和取舍逻辑写出来。
- 编程题不处理输入边界。空链表、容量为0、key不存在、并发调用。这些边界就是笔试和面试都爱深挖的细节。
- 没看清楚“手写全代码”还是“写伪代码”。如果要求手写全代码,就不要用注释代替实现;如果只是思路题,也不要花大量时间抠代码细节,输出结构完整的设计更重要。
除此之外,还有一个容易被忽略的坑:代码题里使用了C++的map、Go的map或Python的dict,要留意线程安全性。如果题目要求多线程场景,使用非并发安全的数据结构一定要自己加锁。这一点很容易让阅卷人觉得你缺乏工程经验。
4.3 留给后来人的备战清单
如果你想认真准备这类核心系统工程师笔试,我建议按以下清单复习,顺序也是优先级从高到低:
- 操作系统:进程线程、调度、死锁、虚拟内存、页面置换、IO模型、零拷贝。做到能不看笔记画出整个数据流。
- 计算机网络:TCP状态流转图、握手挥手、TIME_WAIT、拥塞控制、epoll的LT和ET。这是笔试和面试双高频。
- 数据结构与算法:LRU手写、TopK、布隆过滤器、哈希表、链表操作。至少完成LeetCode上关于LRU、LFU、前缀树这些经典题。
- 分布式系统:CAP、BASE、Raft选举、分布式锁、一致性哈希。不用追求源码级理解,但关键流程要能背能画。
- 语言底层:C++的new/malloc、智能指针,Go的goroutine调度、channel、sync包。选一个主语言深入,另一个了解核心概念。
- Linux基础:常用命令、awk/sed、性能排查工具top/vmstat/iostat。有些选择题会直接考命令输出含义。
这些内容不算多,但每个点都要能“讲给别人听”。我复习时有个笨但有效的方法:每学完一个模块,假装自己在给室友讲题,用大白话把原理说清楚。如果讲到一半卡住,说明理解还没到位,回头再看。
个人体会
这批笔试给我的最大感受是:它不像普通校招卷那样考“你刷过多少题”,而是考“你有没有真的理解计算机系统”。很多题没有绝对标准答案,比如分布式锁该选Redis还是ZooKeeper,只要你能把权衡讲清楚,就能拿分。所以准备过程中,不要只记结论,要多问“为什么”和“在什么场景下会出问题”。
最后再分享一个小技巧:刷题遇到不会的知识点,不要马上看答案,先凭已有知识写一个“我认为的答案”,再对照资料修正。这个过程留下的印象比直接背答案深刻得多。祝备战校招的各位顺利拿到心仪的offer,如果这篇复盘对你有帮助,那就是它最大的价值了。