拓冰建站拓冰建站
首页 / 资讯中心 / 正文

2025-2026嵌入式面试高频考点:C语言、Linux驱动、设备树与调优

前阵子帮朋友的公司筛了几轮嵌入式岗位的简历也替几个技术群里的兄弟做过模拟面最大的感受是嵌入式开发的面试题这两年换了一遍底子。以前拿一块开发板点个灯、跑通串口、能把厂家 SDK 里的例子调起来基本就能聊下去现在同样的岗位面试官会顺着问你中断上下文里能不能睡眠、设备树的 compatible 是怎么匹配到驱动的、模型量化之后精度掉了几个点怎么补。标题里提到的 2025-2026 年高频知识点不是谁拍脑袋总结出来的它跟这两年的产业需求直接挂钩——芯片算力上来了Linux 方案的成本下来了边缘侧要跑的东西变多了面试自然就往这些方向压。这篇内容我打算按“面试官为什么这么问”和“候选人该怎么答”两条线一起走把 C 语言底层、Linux 应用与驱动、设备树、系统裁剪、算法部署、性能调优、工具链这几块串起来。适合正在准备跳槽的初中级工程师也适合带团队的人拿去当内部培训的提纲。里面提到的参数、命令、代码片段都是我在实际项目和面试复盘中反复用过的能直接拿去试。1. 面试风向变了2025-2026 年嵌入式岗位到底在考什么1.1 从“会点灯”到“能定位”评价标准的迁移早些年面试嵌入式考核链条大概是“会用工具—能看懂原理图—能调通外设”。现在这条链子被拉长了面试官默认你会用工具所以工具本身不占分占分的是你遇到一个跑不起来的板子能不能在半小时内把范围缩到某一段代码或者某一个硬件配置上。我把这种迁移拆成三层。第一层是“能跑”也就是功能实现这部分现在权重很低因为开源示例太多抄都能抄出来。第二层是“能解释”你要说清楚为什么这里用自旋锁而不是互斥锁为什么这个缓冲区要按 cache line 对齐为什么这个延时用忙等而不用睡眠。第三层是“能定位”给你一段崩溃日志、一个 oops、一串 perf 数据你要能顺着线索找到根因。第三层是分水岭。我面过一位候选人简历上写了三个 Linux 项目问他系统偶发卡死怎么排查他回答“重启一下就好了”。这句话一出前面聊得再好也救不回来。反过来有位工作三年的兄弟讲他遇到的 I2C 通信偶发失败从示波器抓波形、到怀疑上拉电阻、再到发现是设备树里时钟频率配错这条完整的排查链讲完面试官直接跳过了后面两道八股题。所以准备面试的时候别再把精力全砸在背概念上。把你自己项目里至少两个“翻过车又修好”的案例从头到尾复述一遍把每一步的判断依据讲清楚这比背二十道八股有用得多。1.2 岗位分层与知识点权重对照不同岗位的考察权重差别很大用同一套复习清单去投所有岗位效率很低。我按常见的三类岗位做了个粗略对照注意这是经验值不同公司会有出入。知识点板块单片机/裸机岗Linux 应用岗Linux 驱动/系统岗C 语言与内存30%20%15%外设与通信协议30%10%15%Linux 应用编程10%35%15%内核与驱动5%10%35%系统裁剪与启动5%5%15%算法部署与调优5%15%5%工具链与工程化15%15%15%这张表最值得看的是最后一行。工具链和工程化在三个岗位里都占到了 15% 左右说明什么说明它已经是通用底座不是加分项。你如果不会写 CMake、不会配交叉编译、不会用 gdb 远程调试那不管投哪类岗位都要吃亏。还有一点权重表里“算法部署与调优”在 Linux 应用岗占到 15%这是近两年才明显起来的。很多做视觉、语音、工业检测的公司应用工程师要负责把算法团队给的模型跑到板子上并且给出帧率和内存占用数据。这块下面单独开一节讲。2. C 与底层基础看起来最老淘汰率最高2.1 指针、内存布局与结构体对齐的连环追问C 语言题是所有嵌入式面试的起手式但现在的问法比十年前刁钻。经典套路是给一段结构体让你算 sizeof然后追问“如果按一字节对齐是多少”“如果加了 packed 属性会有什么副作用”。struct node { char flag; int count; short id; char name[5]; };在 32 位平台上默认对齐下flag占 1 字节后补 3 字节count占 4 字节id占 2 字节name占 5 字节后补 1 字节总共 16 字节。很多人算完就停了面试官接着会问为什么要补这几个字节答案不是“编译器规定的”这么敷衍而是 CPU 访问未对齐地址时可能触发异常或者需要多次总线周期补齐是为了访问效率。接着会问#pragma pack(1)之后变成 11 字节会有什么风险。这里要能说出结构体成员地址不再自然对齐在某些架构上访问会崩或者编译器会插入额外的字节拼装指令导致性能下降。如果再狠一点面试官会问“网络协议包里为什么反而要 packed”这就是场景题了。内存布局那块高频的是让你画出栈、堆、数据段、BSS 段、代码段的位置关系然后问全局变量、静态变量、局部变量、malloc 出来的内存分别在哪。这里有个常见的坑很多人把“未初始化的全局变量”和“未初始化的局部变量”混为一谈。前者在 BSS 段程序加载时被清零后者在栈上值是不确定的。面试官经常用这个问题筛掉一批只会背八股的人。2.2 volatile、static、const、restrict关键字背后的真实考核意图关键字题里volatile的出场率最高但答对率并不高。标准答案通常写“告诉编译器不要优化每次都从内存读”但面试官想听的是三个具体场景硬件寄存器、中断服务程序修改的变量、多线程共享变量。我建议按场景讲。比如你在写一个 GPIO 驱动寄存器地址映射到指针上如果不加volatile编译器可能把连续的读操作优化成只读一次你读到的状态就是过期的。再比如中断里修改了一个全局标志位主循环里检查它如果主循环里的检查被优化进寄存器那这个循环可能永远退不出来。不过这里要主动补一句volatile不保证原子性也不保证内存序。这句话很多人不会说但一说出来面试官会对你另眼相看。多核场景下真正需要的是内存屏障或者原子操作把volatile当同步手段是常见的误用。static的三种用法修饰全局变量、修饰函数、修饰局部变量要能一句话讲清楚区别重点是“内部链接”和“生命周期延长”这两个概念。const常被问到的是“const 修饰指针”的几种写法诀窍是从右往左读const char *p是指向常量的指针char * const p是常量指针const char * const p两个都是。这个口诀不算高明但好用在不出错。2.3 手写题的答题节奏与常见失分点嵌入式手写题一般不会出很难的算法常见的是字符串反转、判断大小端、位操作统计 1 的个数、环形缓冲区、简单的内存池、链表逆序。难的不是写出来是在面试官盯着的情况下写对边界条件。我总结了一个节奏先问清楚约束能不能用库函数、输入是否可能为 NULL、长度是字节还是元素个数再写注释标出边界判断的位置最后口述一遍测试用例。这套流程走下来就算代码有小瑕疵面试官也会认为你有工程习惯。常见的失分点有这么几个。一是忘了判空指针尤其是链表题。二是整型溢出的边界比如int取绝对值时对INT_MIN的处理。三是位操作题里用了有符号数的右移结果遇到负数就出问题。四是环形缓冲区的读写指针判满和判空条件写反导致缓冲区永远差一格或者直接覆盖数据。环形缓冲区那道题我再多说一句它是嵌入式手写题的常客因为能同时考指针操作、边界处理、无锁设计。如果你能主动提出“单生产者单消费者场景下可以不加锁用读写指针加内存屏障实现”这个回答基本就锁定了这轮面试的通过。3. Linux 嵌入式开发三线并进应用、驱动、设备树3.1 应用层进程线程、IPC 与文件 IO 的高频问法应用层的题目看起来最好答但深入问下去很容易露怯。基础的 fork、exec、线程创建、互斥锁这些问的是你会不会用进阶的是问你为什么这么选。比如问“进程间通信有哪几种方式”背下来不算本事关键在于面试官接着问“共享内存为什么通常要配一把信号量”。这里要讲清楚共享内存本身只是把物理页映射到两个进程的地址空间内核不提供任何同步机制所以并发写会撕裂数据。信号量解决的是访问顺序问题但如果两个进程在不同 CPU 上同时写同一块 cache line还会有伪共享导致的性能问题这时候要考虑按 cache line 对齐做数据填充。文件 IO 那块高频的是阻塞与非阻塞、同步与异步的区别以及 select、poll、epoll 的适用场景。这部分建议用数字说话epoll 在处理上万连接时就绪事件的通知是 O(1) 的而 select 每次调用都要把整个 fd 集合从用户态拷到内核态并且有 1024 的默认上限。能顺手说出 LT 和 ET 两种触发模式的区别以及 ET 模式下必须循环读到 EAGAIN这个回答就很完整了。还有一个容易被忽视的点read返回 0 和返回 -1 分别代表什么。返回 0 是正常的文件结束或者对端关闭返回 -1 要区分EINTR和真正的错误EINTR是信号中断需要重试。这个细节在面试里出现的频率比想象中高。3.2 驱动层字符设备到 probe 流程的完整链路驱动岗的面试基本围绕一条主线从设备树里的节点到驱动模块被加载到设备节点出现在 /dev 下到应用层能读写这中间每一步发生了什么。我建议把这条链路背成条件反射。内核启动解析设备树为每个节点创建platform_device驱动注册时通过of_match_table里的 compatible 字符串去匹配匹配成功调用probe在probe里申请资源、注册字符设备、创建类、创建设备节点。static const struct of_device_id my_dt_ids[] { { .compatible vendor,demo-device }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, my_dt_ids);对应的设备树片段demo_device: demo0x10000000 { compatible vendor,demo-device; reg 0x10000000 0x1000; interrupts 0 42 4; clocks clk_gate 3; status okay; };面试官会顺着问reg里的两个数分别是什么答案是起始地址和长度。中断号后面的两个数呢触发类型和标志位。status改成disabled会怎样节点不会被转换成 platform_deviceprobe 也就不会被调用。再往下就是并发控制。中断上下文只能用自旋锁因为不能睡眠进程上下文如果临界区可能睡眠就要用互斥锁。这里有个高频追问“自旋锁加在单核系统上有意义吗”答案是仍然有意义因为中断可能打断临界区而且开了抢占的内核里任务也可能被抢占。能把“为什么不能睡眠”讲到“持有自旋锁时如果睡眠别的 CPU 会一直自旋等待同时可能引发调度器相关的死锁”这个深度就够用了。3.3 设备树配置从看懂到改对的关键细节设备树这两年在面试里的存在感明显上升因为它直接关联到“板子能不能跑起来”这个最实际的问题。面试官常见的问法是给你一段设备树让你指出哪里写错了。最常见的错误类型有四种。第一种是compatible拼错或者和驱动里的字符串不一致导致 probe 不执行。第二种是reg的地址或长度写了但和硬件手册对不上。第三种是引脚复用没配对比如把 I2C 的 SDA 配成了普通 GPIO。第四种是时钟和电源没使能驱动里访问寄存器就直接挂死。调试这四类问题我常用的手段是查/proc/device-tree下面有没有对应节点、节点的属性值对不对以及看dmesg里有没有匹配失败或者资源申请失败的打印。有时候驱动根本没编译进内核用ls /sys/bus/platform/drivers/看驱动列表里有没有你的驱动名一眼就能排除。还有个小技巧设备树里status okay和status disabled是覆盖关系。如果你在板级文件里想改一个已经定义好的节点不用整段复制只要在引用里覆盖属性就行比如uart2 { status okay; };。这样写出来的设备树更易维护面试时提到这一点也能说明你是有实际改板经验的。4. 系统裁剪与启动优化被低估的加分项4.1 裁剪的三条路径内核、rootfs、用户态服务系统裁剪这块很多候选人只会说“用 make menuconfig 关掉不用的驱动”这个答案太浅了。我通常按三条路径来讲。第一条是内核裁剪。重点是关掉用不到的子系统、文件系统、网络协议、调试选项。但要注意有些选项关掉之后驱动会编译不过比如关掉 sysfs 之后很多驱动依赖的类接口就没了。所以裁剪要到“刚好够用”不是越少越好。另外CONFIG_DEBUG_INFO这种选项在生产镜像里必须关掉因为它能把内核体积撑大一倍以上。第二条是 rootfs 裁剪。用 BusyBox 还是直接用 glibc 的发行版这是两条路线。BusyBox 体积小但很多库支持不全用 Debian 精简版方便但体积大、启动慢。折中方案是用 BusyBox 打底按需把要用到的库和工具单独塞进去。strip 掉符号表、删掉文档和本地化语言包、把只读分区压缩成 squashfs这几步做完rootfs 体积通常能压到原来的三分之一。第三条是用户态服务裁剪。这个最容易被忽略但收益往往最大。很多人跑着一个完整的 systemd其实只需要几个守护进程。换成 BusyBox 的 init 或者自己写一个简单的启动脚本启动时间能省下好几秒。裁剪路径主要手段典型收益主要风险内核关闭冗余子系统与调试项体积减少 40%-60%依赖被误删导致驱动编译失败rootfsstrip、去文档、压缩只读分区体积减少 60%-70%缺少运行库导致程序起不来用户态服务换精简 init、去掉无用守护进程启动时间减少 2-5 秒依赖服务未启动导致功能异常4.2 启动时间优化的度量与常见收益点优化启动时间最重要的一步是先测量不测就动手改往往是改了半天没效果还引入了新问题。测量工具我常用三个内核的initcall_debug参数可以打印每个初始化函数的耗时printk时间戳能看出各阶段时间示波器或者 GPIO 翻转能测到硬件上电到第一行输出的时间。常见的收益点有这么几个。一是内核解压用 LZO 或者 LZ4 代替 gzip解压速度快不少。二是延迟初始化把不是必须的驱动改成模块启动后再按需加载。三是并行化把互不依赖的初始化放到不同线程里。四是文件系统从 jffs2 换成 ubifs 在 NAND 上通常更快从 ext4 换成 squashfs 在只读场景下也更快。不过有两点要注意。一是启动时间优化到后面收益递减从 10 秒优化到 5 秒容易从 5 秒到 3 秒就要动很多东西从 3 秒到 1 秒可能得换方案。二是不要为了启动速度牺牲稳定性比如把某些驱动的延时去掉可能在特定批次芯片上就起不来。我在一个项目里为了快 200 毫秒把一个电源芯片的上电等待时间去掉了结果量产时有一批板子偶尔起不来最后加上去不说还赔了一轮返工。5. 算法嵌入式部署与性能调优最后一公里怎么走5.1 量化、算子适配与推理框架选型算法部署这块面试官最喜欢问的是“你的模型在板子上跑多少帧”然后紧接着问“怎么做到的”。如果你的回答只是“用了某框架”这轮基本就过去了。我的回答框架是这样先说模型本身做了什么处理再说用了哪个推理引擎最后给出量化和调优的具体手段。模型侧常见的手段是剪枝、换更轻的骨干网络、降低输入分辨率。推理引擎侧端侧常用的有 TensorFlow Lite、ONNX Runtime、NCNN、MNN芯片厂商一般还会给自家的 SDK比如带 NPU 的芯片通常有配套的转换工具。量化是绕不开的话题。从 FP32 到 INT8理论上模型体积减到四分之一速度提升两三倍但精度会掉。掉多少要看模型和数据集一般图像分类任务掉 1 个百分点以内可以接受检测任务里小目标的召回率可能掉得比较明显。补救的手段是量化感知训练在训练阶段就模拟量化误差比训练后直接量化的效果好不少。算子适配是另一个坑。芯片的 NPU 往往只支持一部分算子不支持的算子会回落到 CPU 执行这时候会出现两种情况要么速度骤降要么精度不一致。排查方法是把模型用可视化工具打开逐个算子看它在哪执行把回落的算子挑出来能替换的替换不能替换的就想办法放到预处理逻辑里做。5.2 性能调优的度量方法别凭感觉说“变快了”性能调优题最能看出一个人的工程素养。我见过太多候选人说“优化之后快了很多”但一问具体数据就答不上来。在 Linux 上做性能分析工具链其实很成熟。perf top看热点函数perf record加perf report看调用栈占比ftrace看函数耗时和调度延迟vmstat和iostat看系统层面有没有瓶颈valgrind的 cachegrind 看缓存命中率。嵌入式环境受限于资源perf不一定能装但ftrace通常内核里就有。我做过一个视频解码的项目一开始帧率上不去团队的第一反应是 CPU 不够。用perf一看热点不在解码算法本身而在内存拷贝上。再查发现是memcpy的目标地址没对齐导致走了慢路径。改成对齐的缓冲区之后帧率直接上了一个台阶。这个案例说明什么说明如果一开始就去优化解码算法方向就错了。还有一个经验性能问题要分层看。应用层、系统调用层、内核层、硬件层每一层的瓶颈表现不一样。应用层看代码热点系统调用层看strace的耗时分布内核层看中断和调度硬件层看总线带宽和内存带宽。用错层次的工具等于拿着体温计去量血压。6. 工具链与工程化VSCode、CLion 与交叉调试6.1 编辑器与 IDE 的插件组合实践工具链这块面试官一般不会直接问“你用什么编辑器”但会从侧面问比如“你怎么管理一个多平台的工程”“怎么保证团队里每个人的编译环境一致”。你要是回答“大家自己配”这个回答就掉分了。我自己的组合是 VSCode 加 CMake 加交叉编译工具链。VSCode 里必装的是 C/C 插件或者 clangd前者胜在配置简单后者胜在索引和跳转体验好。clangd 需要compile_commands.json用 CMake 的CMAKE_EXPORT_COMPILE_COMMANDS选项就能生成。调试用 Cortex-Debug 插件配合 OpenOCD 或者 J-Link能直接在编辑器里打断点、看寄存器、看内存。CLion 我也用它的强项是重构和代码分析尤其是大型 C 工程里找引用、改接口比 VSCode 顺手。它的远程调试能力也不错可以通过 gdbserver 连到板子上调试。缺点是对机器资源占用高在一些开发环境里跑起来比较吃力。不管用哪个核心思路是把编译配置放进 CMakeLists 或者 Makefile把调试配置放进launch.json或者.idea的配置文件然后把这些文件一起提交到仓库。这样新同事拉下代码就能编译调试不用花半天配环境。6.2 交叉编译、gdb 与追踪工具交叉编译是嵌入式的基本功但很多人只会用现成的工具链不知道工具链是怎么来的。面试时如果被问到“你的工具链从哪来的”最好能说出至少两条路一条是用芯片厂商提供的 SDK另一条是用 Buildroot 或者 Yocto 自己生成。前者省事后者可控各有取舍。调试部分远程调试的基本流程是板子上跑gdbserver :1234 ./app主机上用交叉编译工具链里的gdb然后target remote 板子IP:1234。如果程序已经崩了用core dump文件加gdb ./app core也能还原现场。这里要提醒的是交叉工具链里的 gdb 和主机自带的 gdb 不是一回事用错了会连不上或者符号解析失败。追踪工具方面strace看系统调用ltrace看库调用lsof看文件占用netstat或者ss看端口。这几个工具在排查“程序为什么卡住了”这类问题时特别好用。有一次客户反馈设备启动后网络不通我们在板子上ss -lntp一看服务根本没起来再查日志发现是配置文件权限不对两分钟就定位了。7. 高频追问实录与避坑清单7.1 面试现场最容易翻车的十个追问这一节我把这些年印象最深的追问整理成表都是那种“答不上来会让面试官皱眉”的问题。追问常见错误回答参考回答方向中断里能调用 sleep 吗能加个延时不能中断上下文不可睡眠malloc 失败怎么处理直接返回检查返回值做降级或回收内存泄漏怎么查凭经验看代码valgrind、mtrace、重载分配函数优先级反转怎么解决加锁就好优先级继承或优先级天花板段错误如何定位加打印core dump 加 gdb 看栈回溯多线程共享变量要加锁吗都要看是否原子访问注意内存序为什么用 DMA速度快减少 CPU 占用注意缓存一致性内核态和用户态区别权限不同地址空间、权限、可睡眠性都不同编译报符号未定义加个头文件检查链接顺序和库依赖程序占用内存越来越大内存泄漏区分泄漏、碎片、缓存增长拿“优先级反转”这一条说吧这是实时系统里的经典问题但很多做 Linux 应用的候选人不熟悉。场景是低优先级任务持有锁高优先级任务等锁中优先级任务抢占了低优先级任务导致高优先级任务被无限期阻塞。解决办法是优先级继承让低优先级任务临时继承高优先级尽快把锁释放。能把这个场景讲清楚说明你对实时性有概念这在做工业控制的团队里很吃香。7.2 项目讲述的坑与应对策略最后一个大坑是项目讲述。很多人聊项目的时候从头到尾在讲“我用了什么技术”但面试官想知道的是“你解决了什么问题、怎么判断方案好坏”。我建议用“问题—约束—方案—验证”这个顺序讲。问题是什么比如设备在高温环境下偶发重启。约束是什么比如不能增加成本、不能改硬件、时间只有两周。方案是什么比如怀疑电源纹波加了滤波电容并优化了软件的上电时序。验证是什么比如做了高低温循环测试一百次没再复现。这个结构的好处是面试官能顺着任何一个环节追问而你每个环节都有准备。最怕的是那种“我们用了 XX 框架性能提升很明显”的空话一问“提升多少”“怎么测的”“有没有副作用”就卡住了。另外提醒一句别把团队成果说成个人成果也别把别人的模块说成自己写的。面试官追问几个细节就能试出来一旦发现夸大信任就没了。真实的项目经历哪怕技术含量低一点只要排查过程讲得清楚照样能拿到高分。我个人在实际带人和面试中的体会是这两年嵌入式岗位真正稀缺的不是会多少框架的人而是能在信息不全的情况下做判断的人。板子起不来没人告诉你哪里错了你得靠日志、靠示波器、靠对系统分层结构的理解一层层把范围缩小。这种能力没法靠背题速成但可以通过复盘自己的项目慢慢养出来——每修好一个 bug把从现象到根因的每一步判断记下来半年之后你会发现面试时能讲的东西突然就多了。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门