滴滴Linux内核岗内推笔试全复盘:考点梳理与备考指南 📅 发布时间:2026/8/30 7:21:41 👁 浏览次数: 2018年秋天我投了滴滴的校招内推岗位是Linux内核工程师。坦白讲那时候网约车平台对内核岗的需求不像现在这么透明很多人问我“滴滴不是做App的吗招内核工程师干嘛”。等你真正进入笔试环节看到那份卷子就会明白越是业务规模大的公司越需要在底层做文章。这份笔试给我的整体感觉是——不浮夸不炫技但每一道题都在考你对Linux内核真正理解到什么程度有没有做过实际项目有没有在线上环境踩过坑。这篇文章我拖了很久才写主要是因为当时很多题我现在只能凭记忆复盘没办法做到一比一还原。但题型分布、考察重点、答题思路包括后来我和面试官交流时确认过的考察意图我都记得比较清楚。整理出来一方面给自己做个记录另一方面也给后面想投内核方向、基础架构方向的同学一份参考。不管你是准备校招、社招还是单纯想系统学Linux内核这份复盘里涉及的考点和思路都应该对你有帮助。全文不讨论具体薪资和公司内部信息只说技术本身。1. 内推笔试的整体定位与考察逻辑1.1 为什么网约车平台需要Linux内核工程师很多人低估了出行平台对底层技术的依赖程度。2018年的时候滴滴的订单量已经是千万级甚至更高的规模后端服务遍布多个机房容器化正在大规模推进业务对网络、存储、计算资源的需求非常苛刻。在这种量级下很多问题已经不是“加机器”能解决的了。举个最直观的例子司机端和乘客端的实时位置上报、订单状态同步这些请求非常频繁且对延迟极其敏感。如果内核协议栈的参数没调好或者网络中断处理路径上有性能瓶颈用户感知到的就是“App卡了”“订单下发慢了”。再比如一个机房里的万兆网卡收包软中断集中在某个CPU核上业务延迟就会飙升。这些问题都需要懂内核的人从底层去优化。所以滴滴招Linux内核工程师目标很明确不是让你来写业务代码的而是让你来保证整个底层基础设施的稳定性和性能。笔试的考察重点也自然围绕着“你是否具备在真实生产环境下与内核打交道的能力”来设计。1.2 笔试结构与时间分配策略这份笔试给我的第一印象是题量不算特别大但每道题都需要认真思考两个小时的时间其实很紧张。整体结构大致是这样分布的选择题考察基础概念覆盖面广涉及进程、内存、文件系统、网络协议栈。简答题考察对某个机制的原理描述比如“请简述缺页异常的处理流程”。代码分析题给一段内核相关代码片段分析它的行为和潜在问题。综合设计题给一个业务场景让你设计内核层面的优化方案。在内推笔试这种环节出题人通常不会故意刁难你但会在一道题里埋好几个知识点看你能否全部识别出来。比如给你一段spinlock的代码不仅考锁的使用还考中断上下文、死锁预防、性能和安全的权衡。如果你只是背过“自旋锁会忙等”这种结论没有真正理解背后的约束关系很容易在细节上丢分。我个人的建议是先做自己最有把握的题把该拿的分稳稳拿到再回头啃难题。不要在一道代码分析题上死磕超过十五分钟那会影响你后面大题的时间。2. 高频考点拆解进程调度与内存管理2.1 进程调度CFS如何实现公平进程调度是内核笔试的必考方向滴滴这份卷子也不例外。选择题里考了CFS完全公平调度器的基本原理简答题里则问到了“CFS是如何实现公平性的”以及“nice值与权重的关系”。这里我建议不要只背结论要理解CFS的设计逻辑。CFS放弃了传统的时间片概念引入了一个虚拟运行时间每个可运行实体按照其权重推进自己的虚拟时钟。权重越高虚拟时钟走得越慢获得CPU的时间就越多。数据结构用的是红黑树每次选择虚拟时间最小的那个进程来运行。nice值在这个体系里的作用不是简单加减时间片而是转化为权重值。内核里有一个prio_to_weight数组每差一个nice级别权重大约差1.25倍。举个例子两个进程nice分别为0和1它们获得CPU时间的比例大约是1.25比1。这不是靠给进程划分固定时间片实现的而是通过不同的时间流逝速度自然形成。答题时如果能补充上调度延迟和最小粒度这两个概念会显得你理解更深。内核里引入了sched_min_granularity_ns和sched_latency_ns这两个参数用于保证调度延迟在合理范围内同时避免进程切换过于频繁。我当时把这两个参数的相互制约关系讲清楚了这应该是加分项。2.2 内存管理从虚拟内存到Buddy系统内存管理在内核笔试题中出现频率非常高考点也相对集中。我记得滴滴这份卷子涉及了虚拟地址空间布局、内核态和用户态的划分、Buddy分配器和slab分配器的区别。虚拟地址空间这块最常见的一个坑是很多人分不清“虚拟地址”“物理地址”“内核虚拟地址”的关系。内核里有一个经典的地址映射关系内核空间直接映射区线性映射区里虚拟地址和物理地址之间是简单的偏移关系而在vmalloc区、模块加载区、固定映射区映射关系则要复杂得多。Buddy系统解决的是物理页框的分配和释放问题它以2的幂次为单位管理空闲页块通过伙伴关系进行合并和分裂。很多人会忽略一个细节Buddy分配器本身并不直接面向内核中大量的小对象分配需求所以内核引入了slab层把小对象缓存起来避免频繁操作Buddy系统。slab层还兼顾了硬件Cache亲和性和对象构造函数这些优化点。笔试时有一道题问“为什么需要slab直接使用Buddy分配小内存有什么问题”这就是在考察你是否理解分层设计的动机。从Buddy角度讲最小分配单位是一个物理页通常4KB为几十字节的小对象分配一整个页框浪费太严重从性能角度讲频繁的页分配和释放要参与全局的分配锁和内存屏障开销很大。slab通过对象缓存完美回避了这两个问题。2.3 缺页异常与页面回收高频简答题滴滴卷子里有一道简答题让我印象很深刻“进程访问一个尚未分配物理页的虚拟地址内核会经历哪些步骤”。这道题考察的是缺页异常处理路径属于看起来简单、实际想答好很难的题。标准回答需要覆盖这几个环节CPU触发缺页异常后保存现场并切换到内核态内核查询异常地址所属的VMA检查访问权限如果VMA合法进入do_page_fault的后续处理通过find_or_create_page等机制分配物理页然后建立页表映射更新TLB最后恢复用户态现场重新执行触发缺页的那条指令。如果到这里就停笔只能拿基础分。高分的关键在于你要能补充各种分支细节如果访问地址不在任何VMA中属于非法访问内核会发送SIGSEGV如果VMA存在但访问权限不匹配比如对只读页执行写操作会触发SIGSEGV或者触发COW如果是堆空间需要扩展还要先通过brk或mmap扩展VMA在物理内存不足时分配页可能触发直接回收或OOM。后来我了解到出题人真实意图是想看候选人对整个内存管理子系统的串联能力而不是死记某一条路径。从用户态malloc到VMA扩展到缺页异常到物理页分配再到页表建立整个链条能讲通说明你真的理解虚拟内存的运作方式。这也是我建议所有准备内核岗笔试的朋友一定要花时间把这条链路的每一步都弄透。3. 从系统调用到文件系统笔试中的Linux抽象层3.1 系统调用的完整路径与上下文切换有一道题给了open系统调用的入口要求描述从用户态调用到内核执行完返回的完整过程。这道题考察的跨度很大涉及库函数、陷入内核、系统调用号、参数传递、返回值处理等多个环节。我当时答题的思路是这样的用户态调用open时实际调用的是glibc的封装函数glibc把系统调用号放入寄存器同时设置好参数寄存器然后执行syscall指令x86_64架构或者int 0x80老式CPU切换到内核态后内核通过syscall入口从寄存器中取出系统调用号和参数进行合法性校验然后根据系统调用表找到对应的处理函数处理完成后返回值放在寄存器里返回用户态glibc再做一次错误处理判断根据errno设置错误码。这里有一个容易忽略的点在参数校验。内核在copy_from_user拷贝用户态缓冲区之前会做指针合法性检查如果用户传入的指针是一个内核地址或者是一个未映射的用户地址就会直接返回EFAULT。另外现在很多系统调用路径上都有tracepoint、seccomp过滤、audit审计等机制的钩子这些机制会引入额外的开销和安全检查在性能敏感代码里需要特别关注。笔试答案里我特意提到了syscall指令相较于int 0x80的优势它专门为快速系统调用设计不需要经过中断描述符表的中断处理流程也不需要在栈上保存太多信息整体开销小很多。这部分应该能让面试官觉得你写过汇编级代码对上下文切换有真实的感知。3.2 VFS从open到具体文件系统的路由逻辑文件系统几乎是Linux内核笔试必考的内容滴滴卷子里有一道选择题考了VFS虚拟文件系统的层次结构还有一道简答题问“open一个文件时VFS如何找到具体的文件系统实现”。把VFS理解成一个“抽象层”很关键。它定义了超级块对象、索引节点对象、目录项对象、文件对象这四个核心抽象。用户态进程对文件的操作会通过VFS转换为对这四个对象的操作再由VFS把请求分发到具体的文件系统ext4、xfs、tmpfs等对应的回调函数。open文件时VFS需要完成路径解析。路径解析会从当前进程的root或cwd开始逐级查找目录项期间会涉及硬链接、软链接、挂载点的处理。每次查找目录都会触发dcache缓存查询缓存未命中才会到实际的磁盘文件系统里去读取目录内容。有一个考点是“挂载点”的处理。挂载点是把另一个文件系统的根目录“嫁接”到当前文件系统的某个目录上路径解析到挂载点时VFS需要切换到被挂载文件系统的根目录继续。理解这个机制就明白为什么mount命令能实现各种文件系统的无缝接入了。我还特意在答题时提到删除或移动一个正在被使用的文件是不可行的因为VFS的引用计数机制不允许这种操作这在业务上其实就是“文件被占用”的根源。笔试之外我想多说一句如果你要做内核方向VFS这套抽象思想非常值得反复揣摩。它把“什么东西都当文件”的Unix哲学变成了一组可扩展的接口理解了它你就理解了Linux系统中许多设计的上层逻辑。3.3 设备驱动与设备模型基础题的重点内容滴滴这份卷子对设备驱动没有考得太深主要是基本概念。选择题里问了platform总线、设备树Device Tree、字符设备和块设备的区别。这其实符合大部分互联网公司内核岗的考法——把常见概念考扎实但不会要求你手写一个完整的驱动。设备驱动在Linux里的典型形态是通过module_init和module_exit注册初始化和退出函数通过file_operations结构体暴露open/read/write/ioctl等操作给用户态通过设备号管理来创建/dev节点或使用devtmpfs自动创建。设备树的作用则是在硬件初始化阶段把硬件拓扑结构描述出来内核在启动时解析设备树为每个节点匹配对应的驱动。这个机制在内核的driver core里和sysfs、uevent等机制深度融合理解它才能理解为什么设备会“自动”出现在/dev下面。我当时答题还补充了关于中断处理的细节设备驱动注册中断处理函数时要考虑上半部和下半部的划分中断处理上半部必须快速完成把耗时操作推迟到下半部softirq、tasklet、workqueue执行。这种划分不是因为编程风格偏好而是因为中断上下文不能睡眠持锁受限且会阻塞同一CPU上其他中断。内核设计里的每一处“别扭”背后都有明确的物理约束和并发约束理解这些约束比记结论重要得多。4. 同步机制与并发控制最容易丢分的一类题4.1 自旋锁、互斥锁与信号量的选型逻辑并发控制类题目是内核岗笔试的试金石。滴滴卷子里有一道代码分析题代码片段里同时用了spinlock和mutex要求分析是否存在问题。这种题看起来是考锁的使用实际考的是你是否理解这两类锁的适用边界。自旋锁的特点是获取不到锁时原地自旋忙等不切换上下文因此它可以在中断上下文和原子上下文中使用。但它在临界区期间禁止了CPU抢占如果临界区代码可能睡眠或者临界区执行时间过长就会严重影响系统实时性甚至导致死锁。互斥锁的特点是获取不到锁时当前进程会进入睡眠状态让出CPU。但因为需要切换上下文所以它不能在中断上下文或原子上下文中使用。mutex的优点是临界区可以比较长并且有优先级继承机制缓解优先级反转问题。我当时把这两类锁的选型逻辑总结成了一段话临界区极小且不允许睡眠时用自旋锁临界区可能睡眠或耗时较长时用互斥锁。凡是看到在中断上下文或持有自旋锁时调用可能睡眠的函数几乎都是必错的点这也是这道代码题的核心陷阱。除了自旋锁和互斥锁信号量、读写锁、RCU也都各有用武之地需要根据读多写少、可睡眠、实时性等需求做权衡。4.2 RCU机制读多写少场景下的最优解滴滴笔试题里有一道简答题问“RCU机制是如何实现无锁读的”。这是内核里比较有区分度的一道题也是我当时答得比较得意的地方。RCURead-Copy-Update的核心思想是读者访问共享数据时不需要加锁直接读取写者需要修改数据时不能直接修改原数据而是先复制一份副本在副本上完成修改然后通过原子操作把新的指针发布出去旧数据什么时候释放呢等到所有可能在读旧数据的读者都离开临界区之后才能安全回收。这里有两个关键技术点一是“宽限期”的判定内核通过观测每个CPU上的quiescent state静止状态来判断读者是否已经离开RCU临界区二是内存屏障的使用写者在发布新指针前的写操作必须对读者可见读者在读取指针后访问数据时数据也必须是最新的。x86架构下RCU发布指针用smp_store_release读者侧用smp_load_acquire编译器不会乱序优化破坏这些语义。业务上读多写少的场景非常普遍比如路由表、配置项、连接状态查询。RCU让读者几乎零开销地并发访问是高性能网络服务里非常重要的一个机制。我在答题时还提了一句RCU和传统的读写锁相比最大的优势是读者路径上完全没有原子操作和锁竞争这在多核高并发场景下尤其重要。4.3 高并发业务场景中同步机制的工程权衡综合设计题里我记得有一道题的场景是“设计一个内核模块用于统计某个网络端口的实时流量要求性能尽可能高”。这种题目没有标准答案但是有很多考察点。最直接的方案可能是每收到一个包就更新一个共享计数器用一个原子变量或者加锁保护。但仔细想一下如果每包都做原子操作多核竞争下性能一定不好。更好的做法是使用Per-CPU变量每个CPU维护自己的计数当用户态需要读取时再把这些Per-CPU的值加起来。这样包处理路径上完全没有跨CPU的锁竞争性能会好很多。另外还可以考虑使用RCU保护统计规则表比如规则表需要动态更新而数据面读写频繁。用RCU可以让数据面读取规则时无锁进行。如果规则多且匹配耗时长还可以考虑更高级的机制比如把部分逻辑下沉到XDP或BPF。答这类题最重要的是展示你理解“业务场景—内核机制—硬件特性”三者之间的关系。没有万能的最优方案只有针对特定场景的合理取舍。面试官想看到的是你有一套权衡的思路而不是背了一堆结论。后来我和面试官交流时也确认了这点他说“这道题就是想看你会不会在方案里滥用锁”。5. 笔试中的实战题网络与容器的内核视角5.1 网络协议栈收包路径上的关键机制网络方向在内核岗笔试里分量不轻因为互联网公司最关心的就是网络性能。滴滴题目里有选择题考了TCP/IP协议栈的层次还有一道简答题问“数据包从网卡到达用户态socket经历了哪些环节”。如果你只回答“网卡中断—驱动—协议栈—socket缓冲区”这个答案太粗了。一个完整的答案需要覆盖DMA从网卡把数据放到内存网卡触发MSI-X中断中断处理函数把包放入软中断队列同时唤醒ksoftirqd或直接在中断返回路径上执行NET_RX_SOFTIRQ内核在软中断里调用驱动poll函数批量收包NAPI机制协议栈处理包括链路层、IP层、传输层最终把数据放到socket接收队列唤醒等待该socket的进程。收包路径上的任何一点都可能成为性能瓶颈。常见瓶颈包括软中断分布不均导致某个CPU核打满可以考虑RPS/RFS锁竞争各层协议处理可能需要共享锁内存拷贝次数多可以用零拷贝技术中断频率过高NAPI的poll机制就是奔着这个去做的。如果再深入可以提到fd的规模增长到一定程度后select/poll模型的O(n)遍历变得不可接受epoll通过事件驱动和回调机制解决了这个问题。我当时答这道题时特意提到了NAPI和RPS因为这两个机制展示了内核针对高流量场景的核心优化思路减少中断次数、让流量均匀分布在多核上。综合设计题里有一道性能调优题基本就是围绕这个思路展开的多写一些协议栈细节会让答案更有说服力。5.2 容器技术依赖的内核能力namespace与cgroup2018年是容器技术在国内大规模落地的时期滴滴的基础设施对容器的依赖已经很深了。笔试里没有直接考K8s但考了容器底层依赖的内核能力这其实是更重要的问题。容器隔离的两个核心手段是namespace和cgroup。namespace让每个容器拥有独立的视图包括PID、网络、挂载点、UTS、IPC、用户等资源视图cgroup则负责资源限制包括CPU、内存、IO、网络带宽的控制。这两个机制都是内核直接提供的所以作为内核工程师如果不懂它们几乎没法做基础架构相关的优化。我印象比较深的一道题是“同一个宿主机上跑多个容器如果某个容器发生了内存泄漏如何保证不影响其他容器”。这不只是运维问题更是一个内核机制题。答案的核心是cgroup的内存限制要把内存上限配置好内核在cgroup达到内存上限时会触发该cgroup的回收甚至OOM杀死该进程组从而保护其他容器。补充一个很多新人不知道的细节cgroup的CPU限额可以配置为“绑核”模式把特定容器的线程绑定到特定CPU核上这可以减少CPU缓存的失效和上下文切换代价是失去了CPU资源的弹性调度。容器技术里很多优化选项本质上是“隔离性”和“资源利用率”之间的取舍理解了这一点你就能理解很多部署配置背后的逻辑。5.3 性能调优类综合题从现象到内核参数有一道综合题让我记忆犹新一个线上服务出现CPU使用率高但吞吐量上不去的情况要求分析可能的原因并提出排查思路。这种题在笔试里很常见但也是最容易答得“空泛”的。我当时的回答分了几条路径先确定为CPU忙还是IO等待如果是CPU忙用perf去采调用栈看是用户态还是内核态的消耗如果是内核态消耗大再看是在锁、软中断、内存回收还是系统调用路径针对不同路径做具体优化比如锁竞争可以用更细粒度的锁或RCU软中断分布不均可以开RPS内存回收频繁可以考虑调整watermark或启用THP。如果CPU利用率不高但是吞吐量上不去要检查是否受限于IO、锁、或者网络延迟。比如报错过多导致频繁的异常处理路径、或者内核日志级别太高导致串口打印拖慢系统这些都是真实线上会遇到的“隐蔽”性能杀手。我还补充过一个小技巧不要一次性堆很多内核参数上去每次改动一个观察效果这在实际排查中能帮你快速定位变量。这类题没有标准答案但不意味着随便写。评分看的应该是两点一是你的排查思路是否系统化能不能从现象一层层收敛到根因二是你是否知道常见问题的合理候选原因。如果你连“先看负载再pvperf”这种基本思路都没有基本就告别这个岗位了。6. 常见问题与备考避坑实录6.1 笔试中的典型失误与失分点回顾我自己的笔试过程和后来跟其他参加过类似考试的同学聊天发现有些失分点非常集中。列成表格给后面的人参考失分类型典型问题改进建议概念混淆分不清自旋锁和互斥锁的使用场景每题都问自己这个上下文能睡眠吗临界区长吗答得太浅只写结论不写推导过程把核心机制的原理链完整写出来别只写一句话思路跳跃写代码分析题直接给结论没有推理过程先描述代码行为再分析约束条件最后给结论忽略扩展没有提到性能、安全、可维护性等考量答完主体内容后补充一段“可能的问题与优化方向”时间分配在难题上卡太久导致简单题没时间写先做会做的再回头啃难题我当时最典型的失误是在一道代码分析题上想得太久导致后面一道简答题写得特别潦草。这个错误很值得吸取。笔试不是竞赛拿满该拿的分比挑战高难度题更重要。6.2 内核笔试备考路线建议如果要给准备Linux内核岗笔试的同学一条清晰路线我建议分三个阶段。第一阶段把基础概念吃透。进程、内存、文件系统、网络、同步机制这五块就是内核的“主干”先把每个子系统的核心机制和核心数据结构搞清楚。这个阶段不需要去看太深层的源码重点是在脑子里建立起完整的功能地图。第二阶段带着问题读源码。不要从头到尾顺序读《深入理解Linux内核》效率太低。更好的方式是从一个具体场景出发比如“我open一个文件到底经历了什么”然后跟着代码走一遍。走源码的过程中你会自然接触到VFS、dcache、inode、文件锁、namespace等大量细节。这种学习方式记忆更牢理解也更立体。第三阶段动手做一些小的内核实验。哪怕只是写一个简单的字符设备驱动或者写一个内核模块去hook某个系统调用都能帮你把纸面上的知识转成动手能力。很多笔试里的代码分析题如果你真的写过一个模块一眼就能看出那段代码用了什么API、踩了什么坑。6.3 面试官真正想从笔试里看到什么我后来陆续参与过一些面试工作也问过几位做内核方向的老同事大家对笔试的理解基本一致笔试考验的先是知识面再是思维方式最后是工程素养。知识面决定了你能不能识别出题里的考点比如看到spinlock就想到中断上下文看到RCU就想到读多写少场景思维方式决定了你能不能把零散的知识串联成因果链比如从内存回收聊到cgroup再聊到OOM工程素养则体现在你会不会主动考虑边界条件、错误路径、性能损耗和可维护性。有内推背景的笔试通常还会结合业务场景出一些“大杂烩”的题。这类题看不到标准答案但恰恰是区分度最高的地方。不要怕答错最怕的是只给一个模糊的方向没有任何具体分析。写清楚每一步怎么做为什么这么做有没有其他备选方案面试官就能看出你的思考深度。我在实际准备和参加这次笔试过程中最大的收获不是拿到内推资格或者offer而是把很多以前“似懂非懂”的知识点真正串成了一张网。如果你正在准备类似的岗位我建议不要迷信“刷题”而是把每一道真题当成一次深入学习的机会。内核领域没有捷径你花时间啃过的每一个机制最后都会成为你理解复杂问题的基础。这份复盘写到最后我还是想强调一点面试和笔试只是起点真正让你在这个领域立足的是你有没有持续探索底层原理的热情和习惯。