寒武纪 Linux驱动开发 二面 面经

1. 介绍一下你做过的最复杂的驱动项目,整体架构是怎样的?

回答思路是从整体到局部,先说清楚项目背景和目标,再说自己负责的模块,最后说架构设计的思考。

例如:做过一个基于 ARM Cortex-A 平台的多传感器数据采集系统,整体架构分三层。底层是硬件抽象层,负责 I2C/SPI 总线驱动、GPIO 中断、DMA 传输;中间是设备管理层,统一管理多个传感器设备的注册、初始化、状态机;上层是字符设备接口层,通过 /dev 节点向用户空间提供统一的读写和 ioctl 接口。

架构设计的核心考量是解耦,底层硬件换了只改硬件抽象层,上层接口不变;新增传感器只需要实现标准接口注册进设备管理层,不需要改其他代码。这种分层思想直接借鉴了 Linux 内核本身的驱动框架设计。

2. 你的项目中 DMA 传输和 CPU 拷贝是如何取舍的?什么情况下 DMA 反而不如 CPU 拷贝?

这道题考察对 DMA 的深层理解,不是"DMA 一定比 CPU 快"这么简单。

DMA 的优势在于大块数据传输时解放 CPU,让 CPU 可以同时处理其他任务,整体吞吐量更高。适合场景:单次传输数据量大(通常 >1KB)、传输频率高、CPU 有其他并行任务要处理。

DMA 反而不如 CPU 拷贝的情况:数据量很小时,DMA 的初始化开销(配置描述符、刷 Cache、等待完成中断)比直接 memcpy 还慢,通常 <64 字节的传输直接用 CPU 更快;数据在 Cache 中已经是热数据时,CPU 拷贝直接命中 Cache,而 DMA 需要先做 Cache 一致性处理,反而引入额外开销;系统中 DMA 通道资源紧张时,等待通道的时间可能超过 CPU 拷贝的时间。

项目中的实际做法是设置一个阈值,小于阈值用 CPU 拷贝,超过阈值用 DMA,这个阈值需要在目标硬件上实测得出。

3. 项目中设备树是你自己写的吗?写设备树时踩过哪些坑?

设备树相关的坑是驱动开发中非常高频的实际问题。

常见踩坑点:

compatible 字符串不匹配:DTS 里写的和驱动 of_match_table 里的字符串有细微差异(多空格、大小写、厂商前缀格式不对),导致 probe 永远不被调用,dmesg 里也没有明显报错,排查起来很费时间。

reg 属性地址单元数不对:父节点的 #address-cells 和 #size-cells 决定了子节点 reg 属性的格式,如果父节点是 #address-cells = <2>,子节点 reg 就需要写两个 cell 表示地址,写错了 platform_get_resource 拿到的地址就是错的。

中断号和触发方式:interrupts 属性的格式因中断控制器不同而不同,GIC 需要三个 cell(类型、号、触发方式),写错触发方式(边沿/电平)会导致中断只触发一次或一直触发。

时钟和引脚复用:忘记在设备节点里引用 clocks 或 pinctrl,驱动 probe 时申请时钟或配置引脚复用失败,设备无法正常工作。

4. 你的项目中有没有做过性能优化?具体优化了哪个瓶颈,怎么定位的?

性能优化题是二面的核心,面试官想看你有没有数据驱动的优化思维。

定位瓶颈的方法:先用 ftrace 的 function_graph 跟踪驱动关键路径的耗时,找到最慢的函数;用 perf stat 看 Cache miss 率,判断是否有内存访问瓶颈;在中断处理函数入口和出口打时间戳,统计中断响应延迟分布。

举一个具体例子:传感器数据采集驱动中发现数据读取延迟偶发性升高,通过 ftrace 发现是 workqueue 调度延迟导致的,worker 线程被其他高优先级任务抢占。优化方案是将 workqueue 改为 high priority workqueue(alloc_workqueue 时加 WQ_HIGHPRI 标志),同时将 worker 线程绑定到指定 CPU 核(WQ_CPU_INTENSIVE + kthread_bind),避免跨核调度开销,最终将 P99 延迟从 8ms 降到 1.5ms。

5. 项目中如何处理驱动和用户空间之间的数据同步问题?零拷贝是怎么实现的?

驱动和用户空间数据交互有三种方式,性能依次提升:

read/write 系统调用:内核缓冲区到用户缓冲区需要一次 copy_to_user,有数据拷贝开销,适合小数据量场景。

mmap 零拷贝:驱动在 mmap 回调中调用 remap_pfn_range,将内核分配的物理内存直接映射到用户进程的虚拟地址空间,用户进程读写这块内存就是直接读写物理内存,没有任何拷贝。适合大数据量、高频传输场景,如视频帧、AI推理输入输出缓冲区。

DMA + mmap 双零拷贝:外设通过 DMA 直接将数据写入驱动分配的缓冲区,该缓冲区同时通过 mmap 映射到用户空间,数据从外设到用户空间全程没有 CPU 参与拷贝,是性能最高的方案。

同步问题:mmap 场景下内核和用户空间共享内存,需要用信号量或原子标志位协调读写,避免用户空间读到未完成的数据。通常的做法是驱动写完数据后通过 poll 机制通知用户空间,用户空间用 epoll 等待通知再读取。

6. Linux 内核的内存分配失败时驱动应该怎么处理?GFP 标志怎么选?

内存分配失败是驱动中必须处理的异常路径,很多驱动 bug

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

嵌入式面试八股文全集 文章被收录于专栏

这是一个全面的嵌入式面试专栏。主要内容将包括:操作系统(进程管理、内存管理、文件系统等)、嵌入式系统(启动流程、驱动开发、中断管理等)、网络通信(TCP/IP协议栈、Socket编程等)、开发工具(交叉编译、调试工具等)以及实际项目经验分享。专栏将采用理论结合实践的方式,每个知识点都会附带相关的面试真题和答案解析。

全部评论

相关推荐

评论
点赞
1
分享

创作者周榜

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