怎么写一个 Linux 驱动?
打开内核源码树。找到Documentation/。翻两页。关掉。打开 Google ,搜"linux driver tutorial"。点开第一个结果——2012 年的博客,代码在新内核上已经编不过了。
这是大多数人的经历。老实说,挺劝退的。
有个 GitHub 仓库叫DriverPractice[1], 158 star , 61 次 commit 。数字不大。但它做对了一件事:把 Linux 驱动开发拆成了 18 个独立的、可编译的、循序渐进的实验——每一个只解决一个问题,上一个搞懂了再进下一个。这种教学节奏,比 99%的"Linux 驱动开发大全"类教程都靠谱。
翻完这 18 个例子,整理出来分享。

01-03 :先让内核认识你
hellodriver——标准开局。init_module()、cleanup_module()、printk()。这跟用户态的printf("hello world")不一样:你的代码跑在内核空间,出了 bug 不是段错误,是内核 panic 。带着这个意识写第一行。
CharacterDevice——字符设备是 Linux 下最基础的设备抽象。/dev/下面的东西大多就是字符设备。这个例子教你注册一个设备号,实现open/release/read/write。这是后面一切复杂驱动的骨架。
UserKernelDataAccess——用户态和内核态的数据怎么传?copy_to_user()和copy_from_user()。一句话:你绝不能直接访问用户空间的指针。为什么?因为那个地址可能根本不在内存里——被换出、被保护、被伪造。内核开发的第一条铁律:信任用户空间传进来的任何东西,等于自杀。
04-07 :把基础设施搭起来
DynamicRegister教你怎么动态申请设备号而不是写死一个——现实世界里没有哪个驱动会硬编码主设备号。PrintkMessageLevel告诉你printk那 8 个日志级别到底怎么用——不是printk(KERN_ERR ...)就够了,优先级搞错的话你的关键信息会被内核默默吞掉。
PrivateData和AutoMakeNode是工程基础。前者教你用file->private_data存驱动私有状态——这是"一个驱动支持多个设备实例"的前提。后者教你让内核自动创建设备节点而不是手动mknod——从 2003 年的 2.6 内核开始就该这么干了,但你翻十个教程有八个还在教手动方式。
这些细节单独拿出来没什么,但合在一起就是一个驱动工程师的分水岭:能不能写出不被同事骂的代码,就看这些基础设施搭没搭对。
08-09 :内核调试和并发——两个最容易翻车的地方
Kprobe——Kernel Probe ,动态插桩。不用重编译内核,在运行时往任意内核函数里插入断点。调试内核模块时你不可能单步调试——kprobe 是你为数不多的"看清楚内核到底在干什么"的手段。
Lock——并发控制。 spin_lock 、 mutex 、 semaphore 、读写锁。内核里的并发跟你以前写的多线程程序根本不是一回事:你写的 spin_lock 可能在任何一个 CPU 上被抢占,你可能在中断上下文里拿锁,你拿错了锁整个系统就卡死——不是进程卡死,是整个系统。内核开发里锁用对了是及格,用错了是事故。
10-13 :和用户态对话
IOCTL是字符设备驱动的标配——用户态程序通过ioctl()系统调用发命令给驱动。关键在于cmd参数的组织:方向(读/写/双向)、数据大小、幻数。这些宏看起来啰嗦:_IOWR(MAGIC, 1, struct my_data)——但每一个字段都在防止用户态和内核态之间的 ABI 错乱。
Multiplexing让你支持select/poll——用户态程序可以同时等多个设备文件,不忙等。这是写一个真正可用的驱动的关键一关:没有 I/O 多路复用,你的驱动在真实场景里根本没法用。
Lseek——文件定位。字符设备默认不支持lseek,但有时候你的设备确实需要"从第 N 个字节开始读"。这个例子告诉你精确怎么做。
14-18 :进阶——符号导出、 GPIO 中断、内核加密
后面几个例子进入了"从写驱动到理解内核架构"的层面。
AccessFileInModule——在内核模块里读写文件。看起来很正常的操作,在内核里其实是禁忌。内核不应该依赖文件系统——但有时候你确实需要加载固件、读取配置。这个例子教你怎么做,更教你为什么不该做。
ExportSymbol——把你的驱动里的函数暴露给别的内核模块调用。内核模块的"API 导出"机制。搭驱动框架的时候会用。
Framework——这是整个仓库的"毕业设计"。前面 17 个例子学了一条条技术, Framework 把它们拼成一个完整的、分层清晰的驱动架构。设备和驱动分离、总线匹配、 probe/remove 回调——Linux 设备模型的三个核心概念,在这一个例子里全串起来了。
GPIO_Interrupt是嵌入式 Linux 的必学内容。Crypto教你怎么调用内核加密 API——哈希、对称加密、异步操作。不是"怎么实现一个加密算法",而是"内核已经帮你实现了,你怎么用"。这是工程的思维:不重复造轮子,用内核提供的基础设施。
为什么这种"小仓库"比大全教程更有价值
DriverPractice 只有 158 star 。跟那些动辄几万 star 的项目比,不值一提。
但它比绝大多数 Linux 驱动教程都好。
不是因为它内容多——18 个例子,每个平均不超过 300 行 C 代码。恰好相反:因为它内容少。每一个例子只做一件事,每一个例子都编得过,每一个例子都不依赖前面你没搞懂的东西。这种克制在教程类项目里极其稀缺。
大多数 Linux 教程的问题是:作者忘了自己当初是怎么学会的。他写教程的时候脑子里装的是四年的经验,下意识跳过了很多"显然"的步骤——但这些步骤对初学者来说完全不显然。 DriverPractice 的作者显然没忘。每一个例子的粒度都卡在"刚好能学到一个新概念"的位置上。
还有一个好处很少有人提:这个仓库的代码量足够小,你可以真正读完。不是收藏夹里吃灰,是花一个周末,从 Hellodriver 敲到 Framework ,一行行在板子上跑。做完之后你不是"看过 Linux 驱动教程"——是你真正写了 18 个驱动。
这区别大了。
参考链接
[1] DriverPractice: https://github.com/starnight/DriverPractice