华为嵌入式面经:机考到 HR 面全流程,串口 DMA 与 RTOS 总线深挖
- 轮次
- 多轮面试合集
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
技术一面(约 50 分钟)
- 栈和堆有什么区别?内存泄漏如何避免?
const int *p与int * const p有什么区别?- C 和 C++ 有什么区别,C++11 用得多吗?
- TCP 和 UDP 有什么区别,粘包、拆包怎么理解?
- 用过哪些数据结构?哈希表的键值冲突怎么处理?
- 树的 BFS 和 DFS 分别是怎样的?
- 手撕:快速选择排序、移除元素、无重复子串。
技术一面 · 项目深挖(串口用 DMA 接收、降低 CPU 占用)
- 为什么用 DMA?不用串口中断行不行?
- DMA 接收不定长数据,怎么判断一帧结束?
- 空闲中断触发时,DMA 还在继续搬数据吗?
- 读取接收长度时新数据又进来了,怎么办?
- 如果连续来了两帧,上一帧还没处理完,会不会覆盖?
- 环形缓冲区由中断写、任务读,怎么保证并发安全?
技术二面 · 启动流程与链接脚本(约 50 分钟)
- .data 段为什么要从 Flash 复制到 RAM?
- .bss 段为什么只需清零、不用存初始值?
- 启动文件里的中断向量表放在哪里?
- 程序能不能直接在 RAM 里运行?
- 改了链接脚本,最容易出什么问题?
技术二面 · RTOS(FreeRTOS 方向)
- 互斥量和二值信号量有什么区别?为什么互斥量一般有优先级继承、信号量没有?
- 优先级反转是怎么发生的?
- 中断里为什么不能直接调用普通队列发送函数?
- 任务栈溢出有哪些表现?怎么定位?
- 一个高优先级任务不阻塞,会发生什么?
技术二面 · 总线与存储
- I2C 为什么要用开漏输出加上拉电阻?总线上两个设备同时发不同电平会怎样?
- SDA 被从机一直拉低,主机怎么恢复?
- SPI 和 I2C 的对比与适用场景是什么?
- I2C 通信画图说明一下:先传什么、为什么这样设计、应答位有什么作用?
- I2C 死锁问题是怎么回事?Linux 下接入十几个传感器怎么处理(设备树角度)?
- Flash、ROM、RAM、EEPROM 有什么区别,分别用在哪里?
- 手撕:快速选择排序。
主管面 / HR 面(约 20 分钟)
- 说说你的职业规划、城市意向,以及你怎么看待团队合作?
- 了解华为的文化吗?能接受高强度加班吗?
- 项目和实习里最难的问题是什么,怎么解决的?
- 最大的一次挫折是什么,怎么调整的?
- 和团队有分歧怎么办?你的想法被别人否定过吗?
- 为什么想进华为?为什么选这个城市(东莞、珠三角等)?
《参考解析》
一面的八股与手撕:答到点上比答得多重要
这轮的结构是自我介绍、挖项目、基础八股、手撕代码,八股本身不深,但每道题都要落到准确的表述上。栈是自动分配、连续、有大小上限,放局部变量和调用帧;堆由 malloc/new 管理、生命周期手工控制、容易碎片。防内存泄漏靠的是申请与释放在同一条路径上配对(包括错误分支提前返回的情况)、用 RAII 或智能指针封装所有权,再配静态检查和 Valgrind、ASan 这类运行时工具兜底。const int *p 是「指向的内容不可改、指针本身可改」,int * const p 是「指针不可改、内容可改」,读法是从标识符往右再往左看 const 落在谁身上。C 与 C++ 的差别在于后者是多范式语言,多了类、模板、RAII、异常、重载与命名空间;C++11 被问到的通常是 auto、lambda、智能指针、右值引用与移动语义、nullptr、范围 for,答的时候最好带一句「在嵌入式里主要用来做零开销的资源封装」。
TCP 与 UDP 一起答:一个面向连接、可靠、保序、按字节流交付,一个无连接、不保证送达和顺序、按报文交付。粘包拆包不是协议缺陷,而是 TCP 把数据当字节流、应用层没有消息边界造成的,解法全在应用层——定长消息、特殊分隔符,或者最通用的「长度字段 + 载荷」。哈希冲突的主流处理是链地址法和开放寻址(线性探测、二次探测、双散列),工程上还要提一句负载因子和扩容策略,因为它们决定了最坏情况的退化程度。BFS 用队列逐层扩展,无权图上第一次到达即最短路径;DFS 用递归或显式栈,适合连通性、拓扑排序和回溯类问题;两者空间开销的区别在于前者要存最宽的一层、后者只存一条路径。手撕题按各自模板过一遍:快速选择复用快排的 partition,只对包含第 k 小的那一侧继续递归,随机选 pivot 可避免有序数据退化到 O(n²);移除元素用双指针原地覆盖并返回新长度;最长无重复子串用滑动窗口加字符最后出现位置,遇重复就把左边界跳到该位置加一。
项目深挖:DMA 加空闲中断加环形缓冲,要答成一条线
「为什么用 DMA」要先说清代价:串口每来一个字节中断一次,高波特率下是中断风暴,CPU 的时间花在进出中断和搬一个字节上;DMA 由控制器直接把数据写进内存,搬完一批才打扰 CPU 一次,占用和时序抖动都明显下降。不用 DMA 当然也能跑,低波特率、小数据量下中断方式完全够用,但你要能指出它的边界在哪——面试官想听的不是结论,而是你知道什么时候该换方案。
不定长数据的帧结束判定有几种成熟路子:协议自带长度字段或帧尾、约定固定长度、用空闲中断(IDLE)配合 DMA 剩余计数换算出本帧实际长度,或者按字符间超时(类似 Modbus 的 3.5 字符间隙)切帧。空闲中断触发时,IDLE 只表示总线空闲满了一个字节时间,此时 DMA 通常已经停下或即将停下,动作是立刻读剩余长度寄存器算出本帧长度,然后马上把「处理」和「继续接收」解耦——要么先停 DMA、要么切换到另一个缓冲。读长度的同时新数据进来、或者上一帧没处理完第二帧就连着来,这类问题的共同根因是单缓冲上「写」和「读」挤在同一块内存里,标准解法是双缓冲(乒乓)或环形缓冲区,让接收指针和处理指针分离,处理不过来时至少能被检测到(记溢出计数、明确丢帧策略),而不是默认一定处理得完。中断写、任务读这个场景本质上只有一个生产者和一个消费者,可以不加锁:写指针只由中断改、读指针只由任务改,保证各自访问是对齐字长的原子操作,用 volatile 防优化,并用内存屏障保证「先写数据、再更新索引」的顺序;实在要在中断里同步,也只能用不会睡眠的手段,互斥量是绝对不能在中断里拿的。
二面底层:链接脚本、RTOS 与总线协议
启动流程这组题问的是「镜像里的初值是怎么到 RAM 里的」。.data 段有非零初值又要可读写,而 Flash 是只读的,所以初值先随镜像烧在 Flash(LMA),启动代码再把这一段拷到 RAM 的运行地址(VMA)上;.bss 段是未初始化或初值为 0 的全局变量,没必要在 Flash 里存一大片 0,只要启动时把对应 RAM 清零即可,这是镜像体积与启动时间的取舍。中断向量表一般放在启动文件最前面的 .isr_vector 段、位于 Flash 起始处,也可以拷进 RAM 并用 VTOR 重定位,常见动机是改向量内容或提速。程序当然能在 RAM 里运行,把代码段链接或拷贝到 RAM 就行,代价是占 RAM,典型用途是擦写 Flash 期间仍需执行代码。改链接脚本最容易出问题的几个点:段的地址与长度和芯片真实的 Flash/RAM 对不上、.data 拷贝与 .bss 清零的符号边界没跟着改、向量表位置错、和 bootloader 的分区重叠、对齐属性不对;这类错误的现场表现往往不是编译报错,而是启动跑飞或直接进 HardFault,所以改完要用 map 文件把每个段的起止地址核一遍。
互斥量与二值信号量的区别要落到所有权上:二值信号量只是「有一个资源可用」的计数,任何任务都能 give,多用于任务与中断之间的同步;互斥量有所有者、谁拿谁放,通常还带递归计数。优先级继承只对互斥量有意义,因为要提升的是「持有者」的优先级,而信号量本身不知道持有者是谁。优先级反转的链条是低优先级任务持锁、高优先级任务等锁,此时一个中优先级任务把低优先级任务抢占,高优先级任务就被间接拖住了;缓解手段是优先级继承与优先级天花板。中断里不能调用普通队列发送函数,是因为队列满时它会阻塞、内部还可能触发调度,而中断上下文不允许这些行为;正确做法是用 FromISR 版本(队列满时返回失败而不是阻塞),并在退出中断时用 portYIELD_FROM_ISR 请求一次切换。任务栈溢出的典型表现是变量被莫名改写、跑飞、HardFault 或偶发异常,定位靠开启栈溢出检查与溢出钩子、用高水位 API 看剩余栈量、按填充魔数扫描栈边界,再对着 map 文件核对;一个高优先级任务如果不阻塞就会一直占着 CPU,低优先级任务永远得不到调度(同级任务仍按时间片轮转),现场表现是别的任务不跑甚至看门狗复位,所以每个任务都得有明确的阻塞点。
I2C 的开漏加上拉是这一面的常青题:开漏输出只能把线拉低、不能推高,高电平靠上拉电阻提供,这样一条线上多个设备是「线与」关系——谁拉低谁有效,从机应答和主机发数据都不会出现一个推高、一个拉低造成的短路。两个设备同时发不同电平其实就是在做总线仲裁:发低的一方赢,发高的一方检测到线上电平与自己发送的不符就退出,硬件上不会损坏。上拉阻值要在上升沿速度和功耗之间折中,挂的设备多、总线电容大了波形会变圆。SDA 被从机一直拉低导致主机认为总线永远忙,标准恢复是发时钟脉冲(bus clear,一般最多 9 个 SCL)把从机剩余的位推完让它放手,再补一个停止位;实在不行就把 SCL、SDA 临时切成 GPIO 手动模拟时序,最后重新初始化 I2C 外设——Linux 里对应的就是总线恢复机制。
总线对比和时序图要能当场讲出来:SPI 是四线、全双工、速率高、没有地址和应答、每个从机一根片选,适合 Flash、显示屏这类高吞吐设备;I2C 两线、半双工、带地址与应答、引脚省、能挂多个设备,但速率较低,而且一条链上某个器件把线拉死会拖垮整条总线,适合传感器、EEPROM 这类低速外设。I2C 的起始位是 SCL 为高时 SDA 由高变低、停止位是 SCL 为高时 SDA 由低变高,把这两个跳变单独规定出来,数据位才只允许在 SCL 低电平期间变化、接收方在 SCL 高电平采样;中间依次是 7 位从机地址加读写位、从机应答、寄存器地址、再应答、数据字节与应答或非应答,最后是停止位。应答位是接收方唯一能明确表达「我收到了」的通道,也是排查器件是否在线时最先看的信号。Linux 下挂十几个传感器,做法是在设备树里给每个从设备写一个子节点,填 compatible 和 reg(7 位地址);真正的难点是地址不能冲突——同型号传感器靠 ADDR 引脚换地址,地址不够或需要在故障时隔离就加 I2C 多路复用器分出子总线,驱动在 probe 里拿到 client 再注册成 iio 或字符设备。存储介质那题按维度答:RAM 易失、按字节随机访问、读写快,放运行期变量;ROM 只读非易失,出厂固化;Flash 非易失但按块擦、按页写、寿命有限,NOR 可随机访问能直接执行代码,NAND 容量大成本低但按页读且有坏块;EEPROM 能按字节擦写、寿命高、容量小,适合存校准参数和配置。选型本质上是容量、擦写寿命、能否字节级改写这三者的取舍。
机考、综合测评与主管面:非技术环节在筛什么
流程是投递简历、机考(约 120 分钟)、综合测评、技术一面、技术二面、主管面或 HR 面,之后还有保温电话与报批。机考与通用软件岗同题库,题面给的样例往往只覆盖部分情况,提交前一定要自己补边界用例(全 0、超长、重复值);策略上是先做简单题快速拿分、把时间留给最后一题,平时多刷相似题型,另外平时太依赖 IDE 补全的人要专门练无补全手写——机考环境里连一个长度判断都要来回改,很吃时间。综合测评是性格测试,没有技术难度,题目会换着说法重复问同一维度,所以关键是前后一致、别自我矛盾,作答贴近「积极向上、情绪稳定、能完成任务、重视团队协作」这类描述即可。
主管面与 HR 面看的是「能不能融进来」,题面不技术但每道题都在核稳定性、抗压性和协作方式:职业规划与城市意向对应「你会不会干两年就走」,加班与文化认同对应「预期是否现实」,最难的问题和最大的挫折对应抗压与复盘能力,分歧与被否定对应的协作边界,为什么选华为和这个城市对应动机是否真实。准备方式是提前备好一两个能深入讲的技术难点案例,把背景、你的动作、结果和反思讲清楚,比临场组织语言稳得多;技术面遇到不会的题,先说思路再补细节,也比硬编更安全。