Linux设备驱动,光听名字就能劝退一拨人。可正是这玩意儿,卡住了无数嵌入式、底层开发者的脖子。最近《手把手教你学Linux设备驱动开发》正式出版,圈里不少人叫它“硬核宝典”。我拿到书翻完之后,第一感觉是:这书终于愿意把驱动开发这事儿掰开揉碎讲人话了。这篇文章我就结合自己这些年写驱动、调内核、带新人的经验,聊聊Linux设备驱动开发到底该怎么学,这本书又是怎么帮你把这条路走通的。
1. 为什么说Linux设备驱动是嵌入式开发的“分水岭”
很多人在应用层写了好几年代码,add、open、read摸得门儿清,可一到内核这儿就发怵。原因不复杂——驱动开发确实是嵌入式Linux领域公认的门槛最高、天花板也最高的方向之一。说它是分水岭,一点不夸张。
1.1 驱动开发到底在解决什么问题
应用程序跑在用户态,硬件资源却被内核态管得死死的。用户程序想往串口发一个字节、想让GPIO拉高拉低、想从摄像头取一帧数据,都没法直接碰硬件。中间这层负责“翻译”硬件能力、并向用户态提供统一访问接口的代码,就是设备驱动程序。
用个生活化的类比:驱动就像水电工。你家(用户程序)不需要知道自来水厂(硬件控制器)内部怎么净化、怎么加压,你只需要打开水龙头(设备节点)就能喝水。水管子怎么铺、阀门怎么接、水质怎么保证,那是水电工(驱动)的事。你家里用起来舒不舒服,取决于这个水电工手艺怎么样。
所以驱动开发解决的核心问题有三类:怎么让硬件被系统“看见”,怎么让硬件能力被用户程序“用上”,以及怎么保证这个过程稳定、高效、不出错。这些问题的答案,散落在内核源码、芯片手册、总线协议和无数实战经验里。而大多数初学者恰恰是倒在了系统化获取这些知识的第一关。
1.2 设备驱动开发的技术栈构成
驱动开发不是单一技能,它是一个高度复合的技术栈。你以为你在写C代码,实际上你同时在跟这几样东西打交道:
- 内核机制:模块机制、字符设备框架、platform总线、设备树、中断子系统、并发与同步机制。没有这些地基,驱动代码压根没法在内核里转起来。
- 硬件知识:看原理图、查芯片手册,理解寄存器、中断控制器、DMA、时钟树、总线时序。写驱动本质上是在用软件操控硬件逻辑,看不懂datasheet寸步难行。
- 工程实践:交叉编译、内核裁剪、模块加载调试、crash日志分析、性能调优。代码写得再漂亮,跑不到板子上都是零。
- Linux应用基础:至少得会用常用命令、会写Makefile、知道文件系统怎么挂载。很多初学者连模块加载失败的内核日志都不会抓,后面自然寸步难行。
所以市面上教Linux命令、教应用开发的书一堆,真正能把这四层串起来讲的教材凤毛麟角。这也是我拿到《手把手教你学Linux设备驱动开发》这本书时最在意的一点——它有没有把这条技术栈完整串联起来。
1.3 为什么驱动开发值得死磕
从功利的角度说,Linux驱动开发相关岗位的薪资在嵌入式领域长期处于头部区间,这背后是真实的人才供需失衡。驱动开发的门槛高,靠谱的人少,企业愿意为“能真正把驱动调通”的人付高价。
从技术成长的角度说,驱动开发是你理解计算机系统整体运作的最佳路径。你写应用代码,系统的复杂性被框架屏蔽了;你写驱动,所有复杂性扑面而来——CPU怎么访问外设、中断怎么嵌套、内存屏障怎么回事、cache一致性怎么维护。把这些东西搞明白再回头看应用层,很多之前模棱两可的底层原理会豁然开朗。我自己带团队招人时,驱动岗出来的人转做任何底层方向,适应速度都明显更快,这就是技术复利。
2. 零基础入门Linux设备驱动:先搞懂这几件事
拿到书先别急着敲代码。我见过太多人第一个星期干劲十足,然后就栽在环境搭建上。磨刀不误砍柴工,入门前的几个基础问题必须想清楚。
2.1 用户态与内核态的边界到底在哪
很多初学者最困惑的问题是:为什么驱动代码不能像普通程序那样直接编译运行?根源就在于处理器提供了不同特权级别,Linux系统将CPU特权级划分为内核态和用户态。
用户态的应用程序跑在低特权级下,无法直接执行特权指令、无法直接访问硬件寄存器。所有涉及硬件的操作都必须经过系统调用陷入内核态,由内核代码统一完成。这就是为什么驱动必须跑在内核态,也解释了为什么写驱动时一个小小的野指针就能让整个系统崩溃重启——因为内核态没有进程隔离保护你。
理解了这条边界,你才能明白驱动开发为什么难:它不是“更底层一点的C语言编程”,而是在一套完全不同、且没有安全网的运行环境中编程。
2.2 内核模块:驱动的基本存在形式
Linux提供了一种机制,允许代码在系统运行期间动态加载进内核,这就是内核模块(Loadable Kernel Module)。驱动开发的所有“helloworld”都是从模块开始的。
一个最简单的内核模块不过几十行代码,但它揭示了驱动运行的完整链路:module_init宏指定入口函数、module_exit指定出口函数、printk输出日志(注意不是printf)、MODULE_LICENSE声明许可证。模块编译后生成.ko文件,通过insmod/modprobe命令加载,通过rmmod卸载。
这里我想强调一个关键认知:模块跑起来之后,它就不再是“你的程序”了,它变成了内核的一部分。它拥有最高特权级,能访问所有内存和硬件。所以你在模块里犯的每一个错——数组越界、空指针、死循环——都不再是“程序崩溃”,而是“系统崩溃”。初学者必须从一开始就建立这种敬畏感。
2.3 交叉编译环境:第一道必须迈过的坎
绝大多数驱动开发不是在PC上直接进行的,而是针对ARM、RISC-V等嵌入式平台。这就要引入交叉编译。
交叉编译的意思很直白:在x86的PC上,编译出能在ARM芯片上运行的代码。这里有个经典大坑:PC上默认的gcc编译出来的是x86指令集,你开发板根本执行不了。必须使用交叉编译工具链,比如arm-linux-gnueabihf-gcc。
为内核模块做交叉编译,还有一点和普通程序完全不同:模块必须与目标平台的内核“配对”。你编写模块时,依赖的是目标板上内核源码里的头文件和编译配置。所以必须先拿到对应版本的内核源码,完成配置(通常用目标板厂商提供的配置文件),编译出内核和模块目录,然后才能在这个环境下编译你自己的模块。
这一步卡住了大量新手。我见过有人拿Ubuntu自带的gcc编驱动,编出一堆莫名其妙的错误,折腾一星期才发现是编译器用错了。这部分内容《手把手教你学Linux设备驱动开发》里用了整整一章来带读者走通环境搭建,步骤给得非常细,几乎到了“保姆级”的程度。
3. 本书核心内容拆解:这套“手把手”课程好在哪
这本书之所以被圈内人称为“硬核宝典”,不是因为它收录了多少冷门技巧,而是它在学习路径的设计上下了真功夫。我快速过一遍它的整体架构,大家就明白它的学习逻辑了。
3.1 从字符设备框架到总线驱动模型:循序渐进的骨架
一个合格的驱动学习路线,理应是递进的。上来就啃网卡驱动、USB协议栈,那是自寻死路。本书采用的路线我比较认可,它基本复刻了一个底层工程师真实的成长曲线:
- 先建立内核模块的基本认知,搞清楚模块怎么编译、怎么加载、怎么调试;
- 然后进入字符设备驱动注册,理解设备号、file_operations结构体、设备节点这几者的关系;
- 接着是并发与同步机制,包括自旋锁、互斥锁、信号量、等待队列、内核线程;
- 再进入platform平台总线模型,理解设备与驱动的分离、匹配机制;
- 然后深入设备树语法与解析流程,这几乎是现代ARM Linux开发的门槛;
- 最后才过渡到硬件实际操作:GPIO控制、中断子系统、内核定时器、DMA、硬件访问。
这套路线的聪明之处在于——每一章都复用上一章的基础,但引入的新概念绝不超过两个。读者不会产生“学了后面忘了前面”的焦虑感,而是在反复调用中把知识内化。我不止一次遇到过自学驱动的人,折腾到platform总线就崩了,回头一看,连最基本的字符设备框架都没吃透。这本书用连续的知识链帮你避免这种断层。
3.2 硬件操作的核心难点是怎么讲清楚的
驱动开发的教学难点,从来不是语法,而是“看不懂硬件”这件事。书里在讲解GPIO控制时,把逻辑说得非常直白:
要控制一个引脚输出高电平,本质就是往对应寄存器的某个位写1。但写之前,你得先通过寄存器配置这个引脚为GPIO功能、设置为输出模式、设置上下拉状态。这三步的顺序错了,引脚动作立刻不对。
这个说法非常实在。很多人以为操作寄存器就是“对着datasheet抄地址”,实则不然。硬件手册里给出的寄存器数量庞大,直接硬背毫无意义,关键要理解“寄存器是硬件的控制面板”这个本质。书里花了大篇幅教读者如何通过readl/writel函数访问内存映射的寄存器,并强调访问I/O内存必须使用Linux提供的内核API而不是直接解引用指针。
再比如中断处理,这是驱动开发中最容易写崩的部分。书里没有直接扔给你request_irq函数,而是先把完整的中断流程走一遍:设备产生中断信号,CPU响应后跳到内核预置的中断向量,内核根据中断号找到对应的处理函数。搞清楚了这个流程,你再写request_irq注册handler,才能理解为什么顶半部要快速完成,为什么底半部机制有软中断、tasklet、工作队列好几种,各自适合什么场景。
3.3 基于“项目制”的动手环节设计
平心而论,“手把手”这三个字不是白叫的。每章节都配套了可运行的完整示例代码,关键是附带了详细的编译指令和运行现象说明。读者照着敲一遍就能跑出预期效果,这种即时反馈对建立信心非常关键。
更难得的是,书里设置了一个贯穿多个章节的综合项目:写一个完整的按键中断驱动程序,配合定时器消抖,并通Linux内核poll机制实现按键事件的异步通知。这就像盖房子,你先学了搬砖、砌墙、搭框架,最后用一栋完整的小房子把知识点串起来。在面试里能给面试官讲明白这种完整项目的人,说服力比贴一堆零散代码强得多。
4. 一条可执行的驱动学习实操路线
书本是地图,真正的路还是要自己走。结合我自己的经历和带新人的观察,我给出一条经过验证的实操路线,它和这本书的思路是匹配的。
4.1 第一步:搭建环境,跑通第一个模块
不要纠结用什么板子。学习初期,一台普通的x86 PC + Ubuntu虚拟机已经足够。Linux内核模块机制是通用的,在PC上编一个hello模块加载到当前内核里,跑通insmod、lsmod、rmmod、dmesg整套流程,你就已经跨过了一半的门槛。
这里有几个我踩过的坑,必须提醒:
- 虚拟机里装的Ubuntu内核版本,必须和Linux内核头文件版本严格一致。检查方法很简单:
uname -r看内核版本,apt list --installed | grep linux-headers看头文件版本,对不上就编不了模块。 - 直接用root操作会更省心,或者全程sudo,不然会遇到权限问题,小事但烦人。
- 用dmesg看内核打印输出,信息量比终端里直接看大得多。很多新手模块加载失败第一时间不是看错误日志,而是疯狂怀疑代码,方向完全搞反了。
跑通hello模块后,立刻自己改造一下:增加一个全局变量,尝试在多个函数里访问它;试着在模块里故意制造一个空指针异常,看系统日志怎么报。这种“主动制造崩溃”的练习能帮你最快的熟悉内核调试手段。
4.2 第二步:写一个完整的字符设备驱动
这是所有驱动开发者的第一个正式作品。关键在于理解四个核心要素:
- 设备号:主设备号标识驱动类型,次设备号标识同类型下的不同设备;
- file_operations结构体:这个结构体是驱动与虚拟文件系统之间的契约,里面的open/release/read/write/unlocked_ioctl等函数指针,就是驱动功能的入口;
- 设备节点:/dev目录下的文件,用户程序通过操作这个文件来和硬件交互;
- 设备类:通过class_create和device_create创建设备类与设备节点,实现设备节点的自动创建。
这个阶段的练习重点是让用户态的open/read/write真正走到你的驱动函数里。可以设计一个简单的实验:驱动内部维护一个环形缓冲区,write往里写数据,read从里面取数据。在PC上跑通这个程序,你对“文件操作如何在用户态和内核态之间流转”就有了具体而微的认知。
这个阶段最容易犯的错误是:没检查copy_from_user/copy_to_user的返回值、没处理并发访问导致数据竞争、没实现llseek导致文件偏移混乱。这些坑非常经典,面试的时候也是高频考点。
4.3 第三步:在Linux内核源码的密林里学会找路
驱动开发到了一定程度,你会发现最大的老师就是内核源码本身。但内核源码几千万行,直接扎进去必迷路。我的建议是学会按需阅读,带着问题找代码。
- 看到一个不认识的函数,用
grep -r "函数名" /usr/src/linux-headers-$(uname -r)/快速定位它的实现位置; - 在源码目录里用
find和grep配合检索宏定义、结构体和关键函数; - 遇到不太熟的子系统,先用
ls看这个目录下有哪些文件,再根据文件名推测功能。
举例来说,当你在驱动里看到container_of这个宏时,一定别跳过。它几乎是内核链表和面向对象式封装的基础设施,搞懂了它,内核代码里的一半谜团就解开了。书里对这个宏的讲解花了大量篇幅配合图示,老实说这是我在其他资料里少见到的详尽程度。
4.4 第四步:从PC过渡到真实硬件板子
PC上写完驱动,终究要过渡到开发板。这一步建议早做,不用等技术大成,只要字符设备驱动熟练了就可以上板。
选板子我给的参考是:不用追求高端,市面上常见的几家国产开发板就行,你要着重的资源是:详细的原理图、芯片手册、出厂Linux内核源码(BSP包)。厂商已经能跑起来的系统和内核源码,就是你学习的最佳参照物。你的第一个目标是:在板子上编译一个hello模块并成功加载。第二个目标:编写一个GPIO点灯驱动,通过操作寄存器的方式点亮LED。
注意这个阶段建议坚持用内存映射寄存器的原生方式操作GPIO,不要一上来就调用内核提供的GPIO子系统API。虽然内核的GPIO子系统和pinctrl子系统封装得很好,是现代标准做法,但原生操作寄存器能帮你彻底看清硬件地址、物理地址、虚拟地址映射的逻辑,这笔基础债迟早要还的,早还不吃亏。
4.5 第五步:啃到设备树,你就摸到了现代Linux驱动开发的钥匙
设备树(Device Tree)是近十年来ARM Linux驱动开发最大的变革。它存在的目的是解决ARM Linux中长期存在的“板级文件混乱”问题:过去每块板子的硬件信息都硬编码在C文件里,升级内核时这些文件频繁冲突,维护成本很高。
设备树的核心思想,是用一种结构化的文本文件,将硬件描述信息从内核源码里分离出来。它描述CPU、内存、总线、外设及其连接关系。当内核启动时,设备树与其绑定的驱动进行匹配,从而知道“当前系统里有哪些硬件、用什么驱动来控制”。
学习设备树有两条线:一是语法,也就是dts/dtsi文件怎么写,compatible属性、reg属性、interrupts属性各自的含义和写法;二是背后的机制,也就是内核是怎么解析设备树并触发驱动与设备匹配的。这两条线缺一不可。只看语法不理解机制,换个平台就抓瞎;只看机制不练语法,连个外设节点都写不对。书里对这两条线的穿插推进处理得非常到位,这也是我认为它最大的竞争力之一。
5. 常用Linux命令与驱动开发调试技巧
驱动开发避不开Linux命令。很多初学者把Linux命令当成背诵内容来学,背完就忘,因为在开发中根本不知道怎么用。我结合驱动开发场景,把最常用的命令重新梳理一遍——它们是调试利器,不是为了应付面试。
5.1 内核模块与系统信息查询命令
uname -a:查看内核版本,是交叉编译模块前必须确认的首要信息;lsmod:列出当前加载的所有内核模块,能看到模块名、大小、引用计数、依赖模块。排查加载冲突时第一眼就看这里;modinfo 模块名:查看模块的详细信息,包括作者、描述、license、依赖模块和参数;dmesg:内核打印日志的宝库,驱动崩溃、加载失败、硬件异常的错误几乎全在这里。配合dmesg | tail -n 50查看最新日志最常用;insmod/rmmod:内核模块的加载与卸载,注意insmod不会自动解决依赖关系,需要手动确保依赖模块先加载。
有个小技巧:dmesg -w可以实时滚动显示内核日志,相当于应用开发的tail -f,调试时挂着它非常香。
5.2 硬件寄存器与内存调试工具
/proc/iomem:查看系统物理内存和IO内存的占用情况。你的设备寄存器映射在哪个物理地址区域,驱动请求资源有没有成功,都在这里看;/proc/interrupts:查看每个中断号的触发次数。驱动中断没触发?看看中断号是否注册成功,中断触发次数有没有变化;/proc/devices:查看当前系统中注册的主设备号与设备名称。注册了字符设备,首先到这里确认设备号是否申请成功;devmem:直接读写物理地址的利器。在调试时,通过devmem工具在命令行直接向寄存器写值,可以快速验证硬件时序与寄存器配置是否正确,不用反复编译加载模块;gpiodetect/gpioinfo:查看GPIO控制器及相关引脚分配状态,用来确认GPIO是否被其他驱动占用,引脚复用是否冲突。
5.3 内核崩溃日志的分析思路
驱动崩溃时最常出现的现象:整个系统panic,屏幕或串口疯狂滚动一大片信息。很多新手看到这个直接蒙了。但panic信息其实已经给出了答案:
- 第一行通常有
Unable to handle kernel NULL pointer dereference at virtual address ...,明确告诉你崩溃原因是空指针;如果是其他类型,会显示paging request、write access等关键词; Internal error: Oops:后面跟的类型是关键;- 调用栈(Call trace)会列出出错指令所在的函数调用链,这是你定位代码事故现场最重要的线索;
pc寄存器(程序计数器)指向的地址加上lr寄存器,能帮你定位到具体函数乃至代码行号。
这时候用faddr2line将内核地址转换为源码文件名和行号,再用objdump反汇编确认指令,定位精度甚至可以精确到某一行C代码。这套技能在实际工作中会大幅加速驱动调试。
6. 设备驱动开发中的典型问题和排查方法
最后分享一些实际开发中的高频问题和排查技巧。这些内容的共性在于,它们不是单纯的知识点,而是操作中反复会撞上的墙。
6.1 模块加载失败:Invalid module format
出现insmod: ERROR: could not insert module xxx.ko: Invalid module format,90%的原因是模块的版本魔法(vermagic)与当前内核版本不一致。
排查思路很简单:
- 运行
modinfo xxx.ko查看模块的vermagic,确认期望的内核版本; - 运行
uname -r查看当前运行内核版本; - 两者不一致说明你用错内核源码了或者编译环境不对。
还有一个常被忽略的原因:内核开启了强制模块版本校验(CONFIG_MODVERSIONS)时,导出的符号都带版本信息,没在完全对应的内核源码下编译的模块直接加载就会报这个错。
6.2 insmod成功但设备节点不出现
新手最常见的困惑:模块加载成功了,但/dev下就是找不到设备节点。这往往是因为驱动里没有调用device_create,或者对应的/sys/class目录下没有成功创建设备类。解决办法是检查cdev_add、class_create、device_create三个环节是否全部成功执行。可以在dmesg里加打印,或在代码里对每个返回值做错误检查。
如果用的是设备树,则要先确认设备树节点是否注册成功、驱动中的compatible属性是否和设备树中的完全一致——包括大小写、下划线和中划线。这个问题非常隐蔽,我见过有人排查了一整天,就是因为设备树里写的是my_device,驱动里匹配的是mydevice。
6.3 中断无法触发,或者中断风暴
中断不触发,原因通常有几个层面:硬件上引脚配置不对、中断号没找对、中断申请时标志位用错、设备树中断属性没配对。最直接的排查手段:写一个简单的测试驱动,在probe里直接申请某个中断号,并通过/proc/interrupts去反复读取触发次数。中断触发了,计数会增加;没触发,计数不变,基本就能排除内核注册环节的问题。
另一种情况是中断风暴——中断一直疯狂触发,导致系统假死。这通常也是底半部机制被频繁调度导致的。此时要把中断处理函数顶半部做的事情尽量精简,中断触发后立刻关闭该中断(通过disable_irq),在后台工作队列中处理完成后再使能中断。
6.4 驱动崩溃后怎么定位是哪一个模块
如果系统日志里有崩溃信息,但你没有在创建崩溃时打印任何调试信息,第一件事是确认崩溃发生在哪个模块。dmesg里会给出一个地址,如[<c0312345>],这通常指崩溃处的指令地址。用addr2line -e 你的内核vmlinux -f 地址就能把地址转换成函数名。如果崩溃发生在外部模块(*.ko),则要用模块加载基址来换算偏移地址,再用模块源码里的符号表来定位。
做驱动开发,一定不要怕崩溃。崩溃是内核学习的最佳教材,每次崩溃都能让你对内存、指针、并发有一个更具体的感知。怕的是崩溃了不知道怎么看日志、不懂怎么定位,白白折腾一通后靠瞎猜。
7. 备考与面试向的额外内容:驱动开发的问题视角
很多人学驱动的一个直接目标是为了面试——嵌入式Linux开发、BSP工程师、内核工程师。从面试视角看,驱动开发的知识点是有明确侧重的,我在参与招聘时比较关注以下几个维度的考查,说出来供大家参考。
7.1 字符设备驱动全流程,却说不清原理
面试让现场写一个基础字符设备驱动,代码完整,但问三个细节就露馅了:
- 设备号的分配方式有哪些?动态和静态分别怎么分配,有什么优劣?
file_operations中read/write的并发访问有哪些处理方式?copy_to_user如果失败,应该返回什么值?container_of的原理是什么?
这些细节都是基本功,也是判断一个人是真的写过代码、还是只是背过模板的分水岭。这本书在字符设备章节的讲解细致程度,足够支撑大家把这些问题答得扎实。
7.2 驱动开发相关的热门面试考点
基于我过去面试候选人的经验,底下这几个考点出现的频率极高,也是驱动开发核心能力的分层检验:
- 竞争与并发:自旋锁、信号量、互斥锁、RCU的使用场景区别。问法通常是“如果中断处理里边要用锁,能用什么锁?为什么?”
- 阻塞与非阻塞I/O:等待队列如何实现进程睡眠与唤醒,poll/select/epoll机制在内核侧是如何实现的。
- 中断上下半部机制:tasklet、workqueue、软中断各自运行在什么上下文,能做什么不能做什么。
- 设备树匹配过程:内核怎么根据设备树节点匹配到驱动,也就是
of_match_table和compatible属性的匹配逻辑。 - 内核内存分配:kmalloc和vmalloc的区别,GFP_KERNEL和GFP_ATOMIC怎么选。
- DMA与缓存一致性:DMA_BUF机制、流式DMA映射和一致性DMA映射的区别,cache同步方法。
这些知识点很多你在写代码时未必会全部用到,但理解它们才是“内核功底”的体现。我带的实习生里,凡是认真把这些机制搞清楚的,后来看任何子系统代码都顺很多。
7.3 面试中最容易翻车的几个技术回答
- “自旋锁和互斥锁的区别是什么”——答“自旋锁是忙等待、互斥锁是睡眠等待”只拿一半分。加一句“自旋锁可以在原子上下文和中断上下文使用,而互斥锁不能在原子上下文使用”,才算答到点子上。
- “Linux设备模型的三层结构是什么”——如果答不上来“总线、设备、驱动”三者的关系,或者讲不清platform总线是如何实现设备与驱动匹配的,基本会被刷掉。
- “设备树的作用是什么”——千万不要只答“描述硬件”,至少要说出:它解决了硬件描述代码与内核源码耦合的问题,提供了一种动态传入硬件配置信息的机制,驱动代码本身不需要随硬件变化反复编译。
8. 关于这本书的实操配套与延伸思考
最后聊一聊这本书的具体使用姿势。我推荐的用法是:不要当小说翻,当实验手册用,每读完一节,就打开电脑把示例跑到板子上。
8.1 配套实验应该怎么做才算到位
这本书的示例代码每个都能跑,但跑通不是目的。我建议做三遍:
- 第一遍照抄代码,跑通功能,感受驱动工作的整体流程;
- 第二遍关掉书,自己从头写一遍,卡住的地方就是你懂没懂的地方;
- 第三遍加上你自己设计的小改动。比如代码里用的是GPIO子系统API,你改成寄存器直操作;比如中断用的是threaded irq线程化,你改成tasklet实现,对比两者差异。
这三遍下来,才叫“吸收”。否则只是看了个热闹,合上书照样不会写。
8.2 板子选择建议
不少读者问我“要不要买开发板、买哪个”。我的建议是:如果手头有x86电脑,前期先不买板子;如果在学完字符设备和并发控制后还有心气进入设备树和硬件实操,再买一块ARM开发板。选板子时注意三大要素:资料全、内核版本新、社区活跃。资料全指原理图和芯片手册要开放;内核版本新指BSP内核不能太老,太老和现在网上主流学习资料脱节;社区活跃指板子出问题能找到人讨论。
8.3 这本书适合谁读
- 有C语言基础和基本Linux操作能力的开发者,从第一章开始学,压力很小;
- 已经会写应用、想往底层转的工程师,这本书的路径设计会帮你把知识尽快体系化;
- 已经写了点驱动但不太成系统的从业者,可以拿这本书查漏补缺,特别是设备树和总线驱动模型这两块,值得反复翻阅。
不太合适的读者只有一种:连Linux基本命令都没上手,指望直接从驱动入门的零基础小白。建议先补一下常用命令、Makefile、C语言指针和链表,再回来学驱动。
最后再分享一点我个人经历中的体会:驱动开发的学习曲线确实陡峭,动辄需要阅读内核源码,去理解那些宏和寄存器定义时甚至会有点崩溃,但这门技能带来的回报同样丰厚。不只是薪资,更是那种“硬件在我的代码掌控之下运转”的成就感。如果你真的决定走这条路,那就把这本“硬核宝典”当作你的地图,一页一页踏踏实实走下来,把每个示例亲手跑通。内核的世界很大,走进去之后你会发现,驱动开发不过是精彩旅程的开端。