点击上方“嵌入式Linux充电站”,选择“置顶/星标公众号”
最近看了一些 27 届同学的准备情况,发现一个很典型的问题:
很多人不是完全不会 Linux 驱动,而是只会背一层。
面试官问:
probe 为什么没有执行?
你答:
compatible 没匹配上。
这个答案不能说错,但大概率接下来就会被继续追问:
那除了 compatible,还有哪些原因? 如果 compatible 是对的,你怎么定位? deferred probe 见过吗? 驱动编进内核和 ko 加载有什么区别?
这时候很多人就开始卡壳了。
嵌入式驱动岗的面试,最怕的不是你不知道某个 API,而是你没有把“设备树、驱动模型、中断、内存、并发、调试”串起来。
下面这 10 个问题,是我觉得 27 届秋招准备 Linux 驱动岗必须吃透的。不是让你背答案,而是要能经得住追问。
1. 驱动的 probe 为什么没有执行?
很多同学第一反应就是:
设备树 compatible 没匹配上。
这只答到了一点。
真正面试的时候,你至少要能从这几个方向排查:
驱动有没有被编译进去看 Kconfig、Makefile、.config,确认是 built-in 还是 module。
module 有没有加载成功如果是 ko,看 lsmod、insmod/modprobe 返回值,以及 dmesg。
设备树节点有没有生效看节点路径、status = "okay"、compatible 字符串、父节点状态。
bus match 规则是否匹配platform 驱动一般看 of_match_table,I2C/SPI 还要看设备有没有被对应 bus 创建出来。
依赖资源是否没准备好比如 clock、regulator、gpio、pinctrl、reset 控制器没有就绪,可能触发 -EPROBE_DEFER。
probe 执行了但失败返回有些人只看“有没有打印”,但 probe 可能进去了,只是在中间申请资源失败后返回了。
比较好的回答方式是:
我会先确认驱动是否加载,再确认设备是否创建,接着看 bus 的 match 过程,最后检查 probe 内部资源申请是否失败。如果是设备树驱动,会重点看 compatible、status、节点路径和依赖资源。实际排查时主要看 dmesg、/sys/bus/platform/devices、/sys/bus/platform/drivers 以及动态调试信息。
面试官听到这个答案,基本会知道你不是只会背 compatible。
2. platform_driver 和 platform_device 是怎么匹配的?
你不能只说:
通过 name 或 compatible 匹配。
要把优先级讲清楚。
一般来说,设备树场景下重点看 of_match_table,也就是驱动里的:
staticconststructof_device_idxxx_of_match[] = { { .compatible = "vendor,xxx-device" }, { }};MODULE_DEVICE_TABLE(of, xxx_of_match);
设备树节点里会写:
xxx@12340000 { compatible = "vendor,xxx-device"; reg = <0x12340000 0x1000>; status = "okay";};
内核启动解析设备树后,会把节点转换成 platform device。驱动注册后,platform bus 会尝试匹配 device 和 driver。匹配成功后,才会调用 probe。
面试官继续追问时,常见方向有三个:
- 如果没有设备树,怎么创建 platform_device?
- name 匹配和 of_match_table 匹配有什么区别?
- 为什么有的驱动明明注册了,/sys 下却没有对应设备?
你要能说出一点:驱动注册不等于设备存在。driver 和 device 是两边,只有两边都准备好,才谈得上 match 和 probe。
3. 中断里为什么不能睡眠?
这个问题看起来简单,但很容易暴露基础。
中断上下文里不能睡眠,核心原因是:
中断上下文没有普通进程上下文,不能像进程一样被调度器安全地挂起和恢复。
所以在中断处理函数里,不能做可能睡眠的操作,比如:
那中断来了之后事情又很多怎么办?
通常要拆成上半部和下半部。
上半部做最少的事情:
下半部再做耗时逻辑:
现在面试更喜欢追问 threaded irq。
如果你能说出:
hard irq 里做快速响应,threaded irq 里可以跑在内核线程上下文,能做相对复杂的处理,也能使用可能睡眠的接口。
这个回答就比只背“中断不能睡眠”强很多。
4. spinlock 和 mutex 怎么选?
最核心的区别:
spinlock 忙等,不能睡眠;mutex 可能睡眠。
所以选择时先看上下文:
中断上下文用 spinlock,不能用 mutex。
临界区很短,且可能被中断访问用 spinlock,并考虑 spin_lock_irqsave()。
只保护一个简单计数可以考虑 atomic 或 refcount,不一定要上锁。
常见追问是:
为什么持有 spinlock 时不能调用 copy_to_user?
因为 copy_to_user() 访问用户空间地址,可能触发缺页异常,可能睡眠。持有 spinlock 时睡眠,会导致严重问题。
你可以这样答:
我会先判断代码运行在哪种上下文,再判断临界区是否可能睡眠。如果可能在中断里访问,不能用 mutex。如果只是进程上下文并且临界区里会调用可能睡眠的 API,就应该用 mutex。
锁这个问题,不是背 API,而是判断上下文。
5. copy_to_user 和 copy_from_user 为什么不能随便用?
字符设备驱动里,read()、write()、ioctl() 很容易遇到这两个函数。
copy_from_user() 是从用户空间拷贝数据到内核空间。copy_to_user() 是从内核空间拷贝数据到用户空间。
它们不能直接用 memcpy() 替代,因为用户态地址不可信:
所以内核要通过专门接口访问用户空间。
面试官经常继续问:
copy_to_user 返回值是什么?
它返回的是没有成功拷贝的字节数,不是成功返回 0、失败返回负数这种普通风格。
所以常见写法是:
if (copy_to_user(buf, kbuf, len))return -EFAULT;
还会追问:
ioctl 里传结构体指针怎么处理?
基本思路是:
这类问题考的是你有没有真正写过用户态和内核态交互。
6. 阻塞 IO、非阻塞 IO、poll/epoll 是怎么配合驱动的?
很多驱动不是用户一读就有数据。
比如按键、触摸、传感器、中断事件,都可能需要等。
如果没有数据,驱动有两种处理方式:
非阻塞模式立即返回 -EAGAIN,让用户态自己决定后续怎么处理。
驱动里通常会用等待队列:
wait_event_interruptible(wq, condition);
事件来了,比如中断里或 workqueue 里:
wake_up_interruptible(&wq);
如果用户态用 poll() 或 epoll(),驱动要实现 .poll:
static__poll_txxx_poll(struct file *file, poll_table *wait){ poll_wait(file, &wq, wait);if (data_ready)return POLLIN | POLLRDNORM;return0;}
面试里你要能说清楚:
poll 不是自己睡眠等数据,而是把当前等待队列注册进去。真正有事件时,驱动唤醒等待队列,用户态 poll/epoll 才会返回。
这个点很多人会混。
7. ioremap 是干什么的?为什么不能直接访问物理地址?
设备树里 reg 描述的是物理地址范围,但内核代码不能直接拿物理地址当指针用。
原因很简单:
CPU 运行在虚拟地址环境下,驱动需要把设备物理地址映射成内核虚拟地址。
所以会用:
base = devm_ioremap_resource(dev, res);
或者老一点的写法:
base = ioremap(phys, size);
之后访问寄存器,应该用:
readl(base + offset);writel(val, base + offset);
而不是直接:
*(volatileunsignedint *)(base + offset) = val;
面试官可能会追问:
为什么要用 readl/writel?
因为 MMIO 访问不是普通内存访问,涉及访问顺序、屏障、架构差异等问题。内核提供 readl/writel 就是为了让驱动以架构无关的方式访问设备寄存器。
这个问题也是区分“写过裸机”和“理解 Linux 驱动”的地方。
8. DMA 一致性问题怎么理解?
最容易被问的是:
CPU 和 DMA 同时访问一块内存,会有什么问题?
核心问题是 cache。
CPU 访问内存时可能走 cache,设备 DMA 访问内存时通常直接访问内存。如果 CPU cache 里的数据和内存里的数据不一致,就会出问题。
典型问题:
- CPU 写了数据,但还在 cache 里,DMA 读不到最新内容。
- DMA 写了数据到内存,但 CPU 读到的是旧 cache。
常见 DMA 内存有两类:
coherent DMA用 dma_alloc_coherent() 申请,CPU 和设备看到的数据保持一致,使用简单,但不一定适合所有场景。
streaming DMA用 dma_map_single()、dma_unmap_single() 等接口,需要在 DMA 前后做同步,适合一次性或阶段性传输。
回答时不要只说“刷 cache”。
更好的说法是:
DMA 一致性的本质是 CPU cache 和设备访问内存之间的一致性问题。Linux 驱动里不应该自己随便操作 cache,而应该使用 DMA API,让内核根据架构处理映射、同步和 cache 维护。
这句话很重要。
9. 设备树里的 pinctrl、clock、reset、regulator 分别解决什么问题?
面试官问设备树,不一定是问 compatible。
他更想知道你是否理解这些资源:
pinctrl配引脚复用和电气属性。比如这个 pin 到底是 GPIO、UART、I2C 还是 SPI。
clock外设模块需要时钟,不开时钟寄存器可能读写没反应。
reset有些 IP 上电后需要释放 reset,否则模块一直不工作。
regulator管电源。传感器、屏幕、WiFi、音频 codec 都可能依赖电源轨。
所以一个外设驱动 probe 里经常会做:
如果你能按这个顺序讲出来,面试官会默认你至少调过板子。
10. 驱动出问题,你怎么定位?
不要回答:
打印日志。
太浅了。
你可以按层次回答。
第一层,看启动和加载:
第二层,看 sysfs:
/sys/bus/platform/devices/sys/bus/platform/drivers
第三层,看中断:
第四层,看设备树:
- reg/interrupts/pinctrl/clock 是否配置完整
第五层,看动态调试:
如果是用户态调用驱动异常,还可以看:
一个比较完整的回答是:
我会先判断问题发生在驱动没有加载、probe 没进、probe 失败、设备节点没生成、用户态访问失败,还是硬件没有响应。不同阶段用不同工具,而不是一上来就盲目加 printk。
这就是项目经验和背八股的区别。
最后说一下:这 10 个问题怎么复习?
建议按这条线串起来:
这条线走通了,Linux 驱动面试的大部分问题都能接住。
很多同学准备秋招时,会把时间花在背 API 名字上。但面试官真正想看的,是你能不能把一个外设从设备树、probe、寄存器、中断、用户态接口到问题定位完整讲下来。
如果你现在只能零散回答,那就别急着刷更多题。
先把这 10 个问题吃透。
能经得住追问,才算真的会。
知识星球,如果你也需要一个良好的学习环境,加入星球,跟我一起进步。这是一个笔试刷题+面试取经+就业指导+经验分享的私密圈子,你可以阅读星球专栏、向我提问,一起进步成长。(当前已涵盖大疆、小米、联发科、VIVO、字节、影石、德赛西威、海康威视、理想、蔚来、经纬恒润、美团、芯原股份、全志科技、诺瓦星云、CVTE、科大讯飞、芯动科技)