嵌入式面试为什么一被追问就卡壳:变量位置、中断禁忌、锁与 DMA 缓冲
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
- 这个变量为什么放在这里(放在哪个段、为什么用
static/volatile)? - 中断服务函数里为什么不能这么写(为什么不能阻塞、不能调用
malloc、不能加普通互斥锁)? - 这个锁到底保护了什么?临界区应该包住哪几行?
- DMA 传完之后,缓冲区什么时候才能复用?
- 链接脚本一改,程序为什么起不来?
- 复位之后到进入
main之间发生了什么? - 用过队列和信号量,阻塞、唤醒和超时应该怎么选?
- 项目里写了「实时」,最坏路径和优先级设计是怎么做的?
- HardFault 现场怎么定位?
《参考解析》
变量放在哪里:C 程序的内存分区决定了它的生命周期与可见性。全局变量与 static 变量在 .data(有初值)或 .bss(零初始化),随程序整个生命周期存在;局部变量在栈上,函数返回即失效,所以「返回局部数组的指针」是典型错误;malloc 出来的在堆上,要自己管理释放。volatile 不是「放到某个段」,它告诉编译器「这个值可能被外部改变,每次都要真读真写」——用于寄存器、中断与主循环共享的标志、DMA 目标缓冲区,防止编译器把它缓存进寄存器或优化掉读操作。static 修饰局部变量则是把它从栈挪到静态区,函数多次调用共享同一份。
中断里为什么不能这么写:中断上下文没有独立的线程栈可调度、不能被阻塞,所以:不能调用可能睡眠的函数(malloc、文件 IO、普通 mutex 加锁、printf 的阻塞实现);不能长时间占用 CPU(大循环、延时等待),否则会丢中断、撑爆看门狗;共享数据要保证原子性(用 volatile + 关中断 / 原子指令 / 无锁环形队列)。ISR 里正确的做法是「只做最短的搬运与置位,把重活丢给任务或下半部」,FreeRTOS 里对应 xQueueSendFromISR、xSemaphoreGiveFromISR 这类带 FromISR 后缀的 API。
锁保护了什么:锁保护的是「不变量」而不是某几行代码。回答时要先说出被保护的数据结构及其不变量(比如「队列的 head/tail 与 count 必须同时更新」),再说明临界区必须覆盖所有读写这个不变量的地方,并且所有访问路径都要用同一把锁。常见错误是只锁了写、没锁读,或者用两把锁保护相互关联的两个变量导致死锁。优先级反转也要能提:高优先级任务等低优先级任务持有的锁时,用优先级继承(FreeRTOS 的 mutex 支持)或关中断缩短临界区。
DMA 缓冲区何时能复用:三个条件同时满足才行。① DMA 传输完成标志已置位(或传输完成中断已触发),不能只看「启动了传输」;② 如果用了 D-Cache,要先做 cache 无效化(invalidate)再读,否则 CPU 可能读到旧数据;③ 半传输/双缓冲场景要区分「前半满」与「后半满」,用双缓冲(ping-pong)才能在上半传输的同时处理下半。还要注意缓冲区对齐与长度,以及 DMA 描述符本身不能被编译器优化到栈上(必须 volatile 或放静态区)。
链接脚本与启动流程:链接脚本决定各段放在哪块存储(Flash 的 .text、RAM 的 .data/.bss/栈/堆),以及向量表的位置。改错会导致:向量表偏移与启动地址不一致 → 复位后取不到正确的中断入口;.data 的加载地址(LMA)与运行地址(VMA)没配对 → 全局变量初值是垃圾;栈顶地址超出 RAM → 一用栈就 HardFault。复位到 main 的流程:从向量表取初始 MSP 与 Reset_Handler 地址 → 关中断、初始化时钟 → 把 .data 从 Flash 拷到 RAM、把 .bss 清零 → 调 SystemInit → 进 main。能背出这条链,回答「链接脚本一改为什么起不来」就自然有话说。
阻塞 / 唤醒 / 超时怎么选:等一个必然发生的事件用阻塞(省 CPU);等一个可能不发生的事件必须带超时,否则任务永久挂起、看门狗复位;在中断里只能用非阻塞的 FromISR 版本;多个事件源用事件组或队列集合。原则是「不要无限期等待外部输入」。
实时性与最坏路径:说「实时」要给出最坏执行时间(WCET)的来源:中断响应延迟、临界区最长关闭时间、任务优先级与周期、是否有动态内存分配。优先级设计按速率单调(周期越短优先级越高),并检查可调度性;共享资源用优先级继承避免反转。
HardFault 定位:从栈帧取 R0-R3, R12, LR, PC, xPSR(判断用的是 MSP 还是 PSP),看 PC 落在哪个函数,再结合 CFSR/HFSR/MMFAR/BFAR 寄存器判断是非法访问、未对齐、除零还是取指错误。工程上装一个 HardFault handler 打印这些寄存器,比盲猜快得多。