诺瓦星云嵌入式二面面经
- 轮次
- 二面
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
- 挑一个最有代表性的项目,讲一下整体架构、你负责的模块、遇到的最大难点和最后怎么解决的?
- FreeRTOS 任务间通信方式有哪些?队列、信号量、互斥量、事件组分别在什么场景用?
- Linux 字符设备驱动的基本框架?file_operations、设备号、cdev、platform 总线你了解吗?
- I2C 和 SPI 驱动调试遇到过什么问题?通信失败一般怎么定位?
- 进程和线程的区别?进程间通信和线程同步有哪些方式?死锁怎么避免?
- C++ 多态怎么实现?虚函数表、虚析构、智能指针分别说说?
- 中断上半部和下半部有什么区别?
- 系统跑飞、死机、性能瓶颈一般怎么排查?
- 为什么选择诺瓦星云?你对 LED 显示控制、视频处理方向怎么看?
《参考解析》
- FreeRTOS 的通信方式怎么选:队列用于任务间传数据,自带拷贝与阻塞超时,是最通用的一种;二值信号量多用于任务同步或中断通知任务,中断里只能给不能拿;计数信号量用于管理有限的资源数量;互斥量用于保护共享资源,带优先级继承,能缓解优先级翻转,代价是不能在中断里使用;事件组适合等待多个事件任意一个或全部到齐的组合条件。项目里通常同时用几种——外设数据走队列、共享总线用互斥量、中断通知任务用二值信号量,回答时把「为什么这里不用另一种」讲出来比背定义更有说服力。
- 字符设备驱动的框架:核心是申请设备号、初始化
cdev、把file_operations绑定上去,再实现open/read/write/ioctl/release等接口,最后register_chrdev_region加cdev_add注册进内核。platform 总线是 SoC 内部外设的匹配机制,设备树描述device、驱动侧注册driver,名字或of_match_table匹配成功后进probe,在probe里取寄存器地址和中断号、再注册字符设备或 misc 设备。追问一般会落到「用户态 read 是怎么调到你的函数的」和并发保护,答到file_operations与copy_to_user这条链路基本就够了。 - I2C 与 SPI 通信失败的定位:先分层排除——电源、时钟、复位、引脚复用这些硬件前提先确认,再看设备地址、寄存器配置和时序参数。I2C 可以用
i2cdetect扫地址,用逻辑分析仪看起始位、地址、ACK 和数据位;SPI 主要看片选、时钟极性与相位、位序,以及 DMA 配置。常见原因有地址写错、上拉电阻缺失、时序不匹配、中断没清、DMA 与 Cache 不一致,定位时先抓波形再逐层缩小,比直接改驱动代码快得多。 - 进程线程与死锁:进程有独立地址空间,线程共享进程地址空间但各有独立栈和寄存器上下文。进程间通信用管道、消息队列、共享内存、信号量、socket;线程同步用互斥量、条件变量、读写锁、信号量、原子操作,共享内存配信号量是数据量最大时的选择。死锁避免的思路是打破四个必要条件:统一全局的加锁顺序、缩小锁粒度、用带超时的锁、避免嵌套和持锁调用外部接口。
- C++ 多态与智能指针:多态靠虚函数表实现,基类指针或引用调用虚函数时,运行时按对象的实际类型查表找到真正的实现,所以虚函数调用有一次间接寻址、一般无法内联。基类析构必须写成虚析构,否则通过基类指针
delete派生类对象只会调用基类析构,派生类的资源不会释放。智能指针里unique_ptr独占所有权、开销接近裸指针;shared_ptr共享所有权、靠引用计数归零释放,计数操作有原子开销;weak_ptr不增加计数,用来观察对象或打断shared_ptr的循环引用。 - 中断上半部与下半部:上半部在中断上下文执行,要求快,不能阻塞、不能休眠,通常只做清中断标志、读关键状态、把工作投递出去;耗时逻辑放到下半部,常见实现是软中断、tasklet 和工作队列,工作队列可以睡眠,适合需要调用可能阻塞的接口的场景。追问一般会问「为什么不能在中断里睡眠」——中断上下文没有可被调度的任务结构,睡眠会导致无法被唤醒。
- 系统跑飞与性能瓶颈的排查:先看有没有看门狗复位、HardFault、core dump 或内核 panic 的日志,再用 gdb、
addr2line和栈回溯定位异常点。性能问题先量化再优化:top、ps、ftrace、perf看 CPU 占用与调用热点,实时性用 GPIO 翻转配合示波器测耗时最直观。常见根因是栈溢出、空指针、内存越界、死锁、中断风暴、优先级反转和 Cache 一致性问题;排查顺序是先稳定复现,再二分缩小范围,最后加日志和断言验证结论,别靠猜改代码。