面灵AI→

华为 终端 BG 嵌入式软件面经:三轮技术面,存储与掉电异常路径深挖

轮次
多轮面试合集
时间
2026-09
来源
牛客网

《面试题目》

一面 技术面(约 55 min)

  1. 自我介绍,然后挑简历里参与度最高的项目讲。
  2. 栈和堆分别放什么,局部变量一定在栈上吗?
  3. malloc 出来的内存没有释放,进程退出后会怎么样?
  4. memcpy 和 memmove 有什么区别,内存重叠时会发生什么?
  5. volatile 能保证什么,为什么不能拿它做线程同步?
  6. 编译器乱序和 CPU 乱序有什么区别?
  7. 内存屏障是干什么的,什么场景下需要?
  8. 中断上下文里为什么不能睡眠?
  9. 自旋锁和互斥锁怎么选,自旋锁一直拿不到会怎么样?
  10. 驱动里为什么不能直接用普通指针读写寄存器?
  11. 字符设备驱动从 open 到 read,大概会走哪些流程?
  12. 手撕:写一个固定大小的环形缓冲区,不允许动态申请内存,要支持写入、读取,还要能判断空和满。
  13. 追问:如果中断里写、任务里读,怎么保证数据不会乱?中断里这个锁能不能拿?

二面 技术面(约 70 min)

  1. 挑项目里的存储部分直接提问。
  2. NOR Flash 和 NAND Flash 有什么区别?
  3. Flash 为什么写之前要先擦除?
  4. 页、块分别是什么,为什么不能按字节随便擦?
  5. NAND 为什么会有坏块,坏块一般怎么管理?
  6. ECC 是干什么的,能纠正多少位错误由什么决定?
  7. 什么是磨损均衡,静态和动态磨损均衡有什么区别?
  8. 写放大是怎么来的,为什么小数据频繁写特别伤?
  9. MTD、UBI、UBIFS 分别解决什么问题?
  10. 文件调用 write 返回成功,数据就一定进存储介质了吗?
  11. page cache、脏页和后台回写大概是什么关系?
  12. fsync 到底保证了什么,突然掉电还能不能保证数据在?
  13. 日志文件系统为什么更抗异常掉电?
  14. DMA 读写缓冲区时,为什么要考虑 Cache 一致性?
  15. CPU 已经改了缓存里的数据,但 DMA 读到的还是旧数据,怎么处理?

三面 技术面(约 50 min)

  1. 场景设计:设备每 10 ms 产生一条数据,需要持续写入 Flash;存储空间有限,要循环覆盖旧数据;设备可能在任何时刻突然掉电,但重启后要尽量保住最近的数据。

追问:

  1. 数据先放 RAM 还是直接写 Flash,分别有什么问题?
  2. 小数据频繁写怎么减少擦除次数?
  3. 缓冲区攒满再写,突然掉电时缓存里的数据怎么办?
  4. 怎么判断一条记录是不是完整写入的?
  5. CRC 能发现数据损坏,但怎么判断哪份元数据是最新的?
  6. 索引写一半掉电,重启后怎么恢复?
  7. 双备份元数据如果两份都不一致,信哪一份?
  8. Flash 写满以后怎么回收,回收时又掉电怎么办?
  9. 某个块突然变成坏块,原来的数据怎么迁移?
  10. 重启后不能扫描整块 Flash,怎么把恢复时间压下来?
  11. 看门狗在写入中途复位,和直接断电有什么区别?
  12. 这种掉电场景你准备怎么测试,总不能真靠手拔电源吧?

《参考解析》

  1. 先看清三轮的分工,复习的重点跟着变:一面查基础(C、操作系统、驱动),二面往存储底层挖,三面基本不给背题的机会——面试官抓着一个异常场景一直追,你说加锁他问突然掉电怎么办,你说落盘他问设备缓存没刷完怎么办。所以备考时不能只背 Flash、文件系统这些定义,真正拉开差距的是异常路径:写到一半怎么办、掉电怎么办、数据不一致怎么办。每准备一个机制,都顺手想一遍「它在哪一步会被打断、打断后怎么恢复」。

  2. 一面的 C 与并发细节要答到点上:局部变量不一定在栈上——被优化进寄存器、加 static 进静态区、或者被编译器提升位置都可能;堆上是 malloc 出来的对象,生命周期由程序管理;栈是自动分配、连续、有大小上限。malloc 不释放时进程退出,操作系统会回收全部地址空间,所以「进程退出会不会泄漏」的答案是:单次进程退出不会,但长生命周期进程里反复申请不释放会持续增长,服务类程序必须靠 RAII 或显式释放。memcpy 与 memmove 的差别只在重叠场景:memcpy 不保证重叠正确(实现可能按字批量拷贝导致数据被覆盖),memmove 会判断方向做前向或后向拷贝。

  3. 乱序、屏障与中断上下文这组题是驱动岗的筛选线:编译器乱序是编译期指令重排(受语言内存模型与 volatile 约束),CPU 乱序是运行期的动态调度与缓存可见性,两者都可能让多核或 DMA 场景看到不一致的中间状态,所以需要内存屏障(编译屏障约束编译器,硬件屏障保证读写顺序与可见性)。中断上下文不能睡眠,是因为它没有可被调度挂起的任务上下文——一旦睡下去就没有人来唤醒调度它,内核会直接报错,所以中断里只能用自旋锁、原子操作和 FromISR 类接口。自旋锁一直拿不到会持续占用 CPU,单核上还会因持锁者无法被调度而死锁,因此临界区要极短;互斥锁可睡眠、适合可能长时间持有的场景。

  4. 驱动读写寄存器必须用带 volatile 语义的访问函数:普通指针读写在编译期可能被优化掉或合并,运行期还可能因乱序导致读写顺序与预期不符,位域写法在不同编译器下布局也不一致。正确做法是 ioremap 映射后使用 readl/writel 之类的访问器(它们内含屏障语义),并注意端序与位操作的可读性。字符设备从 open 到 read 的主链是:用户态系统调用进入 VFS,VFS 按设备号找到 cdev 并调用注册的 file_operations,open 完成私有数据初始化,read 从内核缓冲区把数据 copy_to_user 回用户空间;这条链路里错误返回、并发保护和用户指针校验都是考点。

  5. 环形缓冲区的追问是这一面最见功底的地方:基础版用 head、tail 两个索引加容量取模,判断空用 head == tail、判断满通常留一个空位((tail+1)%size == head)或用计数变量,注意不能用动态内存。追问「中断里写、任务里读」时,标准答案是识别出这是单生产者单消费者场景,可以不加锁:读写索引各自只被一方修改,用 volatile 保证不被优化,用编译屏障或读写屏障保证索引更新与数据写入的顺序(先写数据再更新索引,读侧先读索引再读数据),并让索引宽度与访问是原子的;中断里绝对不能拿互斥锁(会睡眠),用自旋锁也要评估关中断时间。这题答得顺,一面基本就稳了。

  6. 二面的 Flash 与文件系统要按层讲:NOR 支持随机访问、可执行代码、擦写寿命较低;NAND 单位成本低、容量大,但只能按页读写、按块擦除且有坏块。写之前必须擦除,是因为擦除状态是全 1、写只能把 1 变成 0,要重新写入 1 就必须整块擦回全 1,这也解释了为什么不能按字节擦、以及为什么擦除是性能瓶颈。坏块来自工艺与磨损,管理方式是在出厂与运行时标记坏块并建立映射(把坏块从可用块池里剔除)。ECC 用于检出与纠正位翻转,纠错能力由校验码位数与块大小决定。磨损均衡分动态(写入时优先挑擦写次数少的块,只均衡被更新的数据)与静态(主动迁移长期不动的冷数据,均衡更彻底但带来额外搬移与写放大)。写放大来自「改一页要搬一整个块」,加上 UBI 的开销,所以小数据频繁写会加速块消耗。

  7. write 返回成功不等于数据落盘,这一串问题本质是缓存层次:write 只把数据写进 page cache 并标脏,真正落盘由后台回写线程异步完成;fsync 会强制把该文件的数据与必要的元数据刷到介质,fdatasync 只保证数据部分。即使 fsync 返回,如果底层设备缓存没刷,掉电仍可能丢——这就是日志文件系统与屏障/FUA 存在的原因。日志文件系统把元数据变更先顺序写进日志,崩溃后用重放保证一致;所以它抗的是「元数据不一致」而不是「数据全不丢」。答题时把这三层分清(page cache、设备缓存、介质),比笼统说「刷盘了就行」可信得多。

  8. DMA 与 Cache 一致性是嵌入式存储岗的常青题:CPU 通过 cache 访问内存,DMA 直接读写物理内存,两者看到的数据可能不同。对策要看架构架构:非一致性(non-coherent)平台上,发送前要 clean(把 cache 里的新数据写回内存),接收后要 invalidate(丢弃 cache 里的旧副本再读内存);同时也别把 DMA 缓冲区放在会被 CPU 预取或被 cache 覆盖的普通可缓存区域,用一致性映射(dma_alloc_coherent)或流式 DMA 映射接口来管理。这题能答到「clean 与 invalidate 各自在什么时机做」,就说明真写过驱动。

  9. 三面场景题的答题框架:日志 + 元数据 + 恢复:这类「持续写、循环覆盖、随时掉电」的题有成熟套路——把 Flash 当环形日志用,每条记录带记录头(序号、长度、CRC)并顺序追加;另设固定位置的双份元数据扇区(记录当前写指针、有效区间、版本计数),写入时先写数据再更新元数据。恢复时用 CRC 判断记录完整性,用元数据里的序号或版本计数选出最新且一致的一份;两份元数据不一致时,靠版本号加记录序号交叉验证,宁可回退到上一个自洽状态也不猜。块写满后回收最旧块,回收过程中的掉电靠「先标记、后擦除、再更新元数据」的顺序保证可恢复。减少擦除次数的手段是攒批、压缩记录、以及把冷数据固定下来做静态均衡。测试不能真靠拔电源,要用断电注入(可编程电源或继电器按随机时刻切断)、看门狗复位注入、坏块注入工具和长时间老化,重点覆盖「写入中途掉电」这类窗口。