新凯来 嵌入式软件开发 二面面经

二面面试官是技术负责人,比一面更注重原理深度和实际项目经验,很多题都是在一面基础上继续往深挖,追问细节比较多。整体节奏偏慢,每道题都会聊很久,建议把自己做过的项目细节提前梳理清楚,光背理论不够,要能结合实际说清楚。

1.一面提到了RTOS,那你讲一下优先级反转是怎么产生的?FreeRTOS是怎么解决这个问题的?

2.你在项目中有没有遇到过栈溢出的问题?当时是怎么发现的,怎么定位的,最后怎么解决的?

3.讲一下Cache的工作原理,Cache Miss会带来什么影响?在嵌入式开发中有哪些场景需要注意Cache一致性问题?

4.你了解内存屏障吗?在什么情况下需要手动插入内存屏障?

5.讲一下Cortex-M系列处理器的异常向量表,NMI和普通中断有什么区别?

6.SPI通信中,如果从机响应变慢,主机这边会怎样?你在项目中有没有遇到过SPI通信异常,是怎么排查的?

7.讲一下嵌入式Linux的启动流程,从Bootloader到应用程序启动,中间经历了哪些阶段?

8.设备树(Device Tree)是什么?它解决了什么问题?你有没有修改过设备树?

9.Linux驱动开发中,字符设备驱动的框架是怎样的?file_operations结构体里你用过哪些接口?

10.讲一下Linux内核的中断处理机制,上半部和下半部分别是什么?tasklet和workqueue有什么区别?

11.你在项目中有没有做过功耗优化?具体做了哪些事情,效果怎么样?

12.讲一下Flash的擦写原理,为什么Flash需要先擦后写?磨损均衡是怎么回事?

13.嵌入式系统中如何实现软件升级(OTA)?需要考虑哪些可靠性问题?

14.讲一下你对内存映射IO(MMIO)的理解,CPU是如何通过地址访问外设寄存器的?

15.如果让你从零开始移植一个RTOS到一块新的MCU上,你会从哪里入手,需要适配哪些部分?

反问环节:

1.二面之后还有几轮流程,大概什么时候能给到结果?

2.团队目前主要做的产品方向是什么,嵌入式这边主要用的是哪个平台?

核心嵌入式面试八股文总结:https://www.nowcoder.com/creation/manager/columnDetail/mPZ4kk

必备嵌入式八股文

🔸 优先级反转与优先级继承

优先级反转:低优先级任务L持有互斥锁,高优先级任务H等待该锁,此时中优先级任务M抢占L运行,导致H被M间接阻塞,实际运行优先级发生倒置。

解决方案——优先级继承协议:当H等待L持有的锁时,系统临时将L的优先级提升到H的级别,使L能尽快执行完毕释放锁,M无法抢占L,H的等待时间得到控制。FreeRTOS的互斥锁(Mutex)内置优先级继承,二值信号量不具备此特性。

🔸 Cache一致性问题

嵌入式中Cache一致性问题主要出现在以下场景:

  • DMA传输:CPU将数据写入Cache后启动DMA,DMA直接读内存可能读到旧数据(Cache未回写);DMA写完内存后CPU读Cache可能读到旧数据(Cache未失效)
  • 多核处理器:两个核各自Cache中缓存了同一块内存,一个核修改后另一个核不可见

解决手段:

  • DMA传输前执行Cache Clean(将Cache数据回写到内存)
  • DMA传输后执行Cache Invalidate(使Cache失效,强制从内存重新加载)
  • 将DMA缓冲区映射为Non-Cacheable区域,直接绕过Cache

🔸 Cortex-M异常机制

Cortex-M的异常向量表存放在Flash起始地址,每个表项是对应异常处理函数的地址。

NMI(不可屏蔽中断):优先级固定为-2,无法被软件屏蔽,通常用于电源故障、看门狗等最高级别的紧急处理。

HardFault:优先级-1,当其他异常处理过程中发生错误,或异常优先级配置错误时触发,是嵌入式调试中最常见的异常入口,可以在HardFault_Handler中读取MSP/PSP寄存器还原现场。

普通外部中断(IRQ):优先级可配置,可以被PRIMASK/BASEPRI寄存器屏蔽。

🔸 嵌入式Linux启动流程

上电复位 → BootROM执行(片内固化代码,初始化基本硬件,加载Bootloader)→ Bootloader(U-Boot)执行(初始化DDR、时钟、串口,加载内核镜像和设备树到内存)→ 内核启动(解压内核,初始化内存管理/中断/调度器,挂载根文件系统)→ 启动init进程(PID=1)→ 执行用户空间初始化脚本 → 启动应用程序

🔸 Linux中断上下半部

上半部(Top Half):中断处理函数本身,在中断上下文中执行,期间关闭同级中断,必须快速完成,只做最紧急的操作(如读取硬件状态、清除中断标志)。

下半部(Bottom Half):将耗时操作推迟到中断上下文之外执行。

  • tasklet:运行在软中断上下文,不能睡眠,同一个tasklet同一时刻只在一个CPU上运行,适合轻量级延迟处理
  • workqueue:运行在内核线程上下文,可以睡眠,可以调用可能阻塞的函数,适合需要等待IO或需要较长时间处理的场景

🔸 Flash擦写原理与磨损均衡

NOR Flash和NAND Flash的存储单元基于浮栅晶体管,写入操作是将电子注入浮栅(编程为0),擦除操作是将电子从浮栅抽出(恢复为1)。由于物理特性,只能将1写为0,不能将0写为1,所以写入前必须先擦除整块(Block Erase)。

磨损均衡(Wear Leveling):Flash每个Block的擦写次数有限(NOR约10万次,NAND约1-10万次)。磨损均衡算法(通常在FTL层实现)将写入操作均匀分散到所有Block,避免某些Block因频繁写入而提前失效。分为动态磨损均衡(只均衡频繁写入的块)和静态磨损均衡(连冷数据块也定期迁移)。

🔸 OTA升级可靠性设计

核心原则:升级失败不能变砖,必须能回滚。

常见方案——双分区(A/B分区):系统有两个完整的固件分区,当前运行A分区,OTA将新固件写入B分区,写完校验通过后修改启动标志切换到B分区启动。如果B分区启动失败,Bootloader检测到启动失败标志后自动回退到A分区。

需要考虑的可靠性问题:

  • 断电保护:写入过程中断电,分区数据不完整,必须通过校验(CRC/SHA)检测并拒绝启动
  • 版本回滚保护(Anti-rollback):防止降级到有安全漏洞的旧版本,通过版本号单调递增机制实现
  • 升级包完整性验证:数字签名,防止恶意固件

🔸 内存映射IO(MMIO)

MMIO将外设寄存器映射到CPU的物理地址空间,CPU通过普通的load/store指令访问外设,和访问内存的方式完全一致,不需要专用的IO指令(区别于x86的port IO)。

ARM架构全部采用MMIO。访问外设寄存器的地址必须声明为volatile,防止编译器将多次读取优化为只读一次(因为寄存器值可能随时被硬件改变)。

在嵌入式Linux中,驱动通过ioremap()将物理地址映射到内核虚拟地址后才能访问,裸机开发中直接使用物理地址。

全部评论

相关推荐

03-31 18:11
已编辑
门头沟学院 前端工程师
理性的太平湖水怪在写...:这是因为很多不是26的想看只能选没offer了吧
双非本科求职如何逆袭
点赞 评论 收藏
分享
评论
点赞
3
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务