1. 分享Linux内核学习和驱动开发的经验。
内核学习
Linux 内核功能越来越完善,如果没有充裕的时间,深入内核并不是很现实。所以建议先读一本内核的书,
第一遍是读,会读的很迷糊;之后反省一下,然后再浏览一下;可以想象一个 OS 是如何运行的,这样可以不
陷入 Linux 内核的细节;最后可以深入自己感兴趣或者需要的那一子系统
推荐 《Linux Kernel Development》
即便是子系统,也是很庞大的。一个省力的方式是网上搜一些相关的文章,便于快速了解这个子系统的运作;
然后结合代码,形成自己的认知,最后做一下总结。如果仅仅是快速了解某一子系统的运作,可以参考一些早期
代码的注解书籍,再深入的时候看看最新的代码实现
对内核的认知是一个反复的过程,一开始并不完善,可能需要反复纠正。不要陷入这种纠错中;而是以后继续
使用和学习过程中,发现了没有弄清楚的地方再深入,毕竟 Linux 内核是不断变化的
还有一个很好的方式是,从系统调用入手,现在这方面的数据不少,而且对系统调用的语义都有讲解,这样可以
间接了解 Linux 系统的一些概念。对系统调用熟悉了,可以根据系统调用的执行过程,来大体了解内核的一个
运作过程;但是跟踪系统调用的时候要注意抓主线,现在内核系统很复杂,一些 code path 上可能会涉及多个
子系统,可以从名字上猜测它们是干什么的,不需要深入,否则会发现精力完全被分散掉了
学习 Linux 内核,一个很重要的是抽象的能力,所谓的抽象这里仅仅是指分清接口和接口的实现。因为 Linux
内核子系统很多,有很多子系统相互渗透,这样 code path 看上去很复杂。阅读代码的时候,为了排除干扰,
需要分清哪些是自己需要看的,哪些是其它子系统的接口,对于其它子系统的接口,先当作它们功能完善不会
出问题好了,这样可以关注重点;打个比方,一个应用程序的代码可能量很大,比如一个 apache 项目,它
包含很多组件,有时候阅读代码的时候会看到不同组件的 API,深入看相关组件实现并不现实,这时候分清主次
对于代码的阅读就很有帮助了,总不能看到了 malloc 就要先把它的实现弄清楚吧,系统调用多者呢
推荐书籍
OReilly.Linux.System.Programming.2nd.Edition
The.Linux.Programming.Interface
驱动开发
一直围绕服务器做,接触的比较多的是网卡驱动。最开始想着从上到下,好好学习协议栈,后来发现内容太多,
进展太慢。后来参考一些驱动开发方面的书籍,把驱动独立开,使用内核提供的接口,就类似写应用的时候很多
情况下只需要了解系统接口和库函数的原型描述而不需要继续深入一样。这样把自己从内核复杂的实现细节中解
放出来,可以重点看网卡的特性部分;之后可以再深入了解设备的运作过程,比如网卡的收发包在协议栈中的
位置和运作
个人感觉如果工作中能接触驱动开发最好,否则很多情况下,有的设备并不常见,比如 Infiniband 卡
现在民用设备越来越广泛,可以选择自己感兴趣而手头又容易有的设备进行研究,比如无线网卡、wifi 等
驱动开发,一定要先专注一个设备,从头到尾熟悉一遍,然后总结驱动开发是怎样的、驱动是如何关联到系统中
的、Linux 采用什么样的分层模式来提供对多种多样设备的支持,如类似 VFS 一样的抽象分层
推荐
linux device driver, 3rd edition
Linux设备驱动开发详解
2. 您觉得Linux驱动开发的难点是什么,有什么好的方法来克服?
Linux 内核对各种设备的驱动开发提供了完善的框架支持,对应某个驱动,把对外的接口弄清楚就可以了。打个
比方,一个设备可能在不同的 OS 上需要支持,比如 FreeBSD/Windows 等,每个 OS 都有自己定义的接口,
设备的驱动定义好与这些 OS 接口的连接,剩下的就是设备本身的特性管理以及驱动接口中对设备管理函数的调
用了,比如寄存器访问、配置管理、缓冲区管理、数据收发等,比较重要的中断和同步的控制,要避免数据处理的
时候的死锁。
比如网卡驱动,基本的要求是提供内核需要的接口,这样网卡驱动才能挂接到系统中,剩下的就是接口需要调用
网卡驱动的内部函数,来对网卡进行控制、数据收发和管理等
Linux 支持的设备种类繁多,不可能所有都掌握,某一子系统也只能是熟悉,因为同类设备还有许多自由的特性。
写驱动的步骤可以概括为:
1) 阅读设备规范,对设备的运行机理有所了解
为了减少干扰,不考虑要支持的 OS,独立与 OS 考虑基本的功能如何实现
2) 参考同类设备在 Linux 内核中的驱动架构
3) 提供基本的 Linux 设备驱动接口和实现设备的基本功能,比如网卡收发小数据量
4) 在性能上逐步提示,比如网卡传输的数据量加大、中断及时处理、避免死锁等
5) 对边界条件进行完善,网卡上就是对一些特殊大小的数据包传输完善等
6) 对设备进行更高级控制的支持,比如网卡支持 ethtool 等工具
7) 反复调试、改进和优化
3. Linux内核有上百个驱动子系统,你研究过内核各种驱动子系统的共性,层次结构设计吗?分享学习一个Linux内核子系统的经验,例如USB、I2C、HID等driver。
每一个子系统都巨大无比,而且涉及各种硬件规范,很难去搞明白所有。只能是遇到问题的时候,
能对某一部分深入下去。之前了解过 SCSI 的架构,最上层的抽象,中间层的桥梁,最底层的设备
驱动控制
如果仅仅是做 driver 的工作,可以把精力放在设备特性上,Linux 内核部分只需要了解驱动
接口和同步、内存管理等基本功能
4. 您深入研究过Linux设备驱动模型,关注过Linux内核驱动的设计思想吗?
Linux 设备驱动模型是从分类的角度来看待设备,分类是多维的,所以 /sys 下也是多个目录
另外,设备驱动模型给出了系统中设备布局信息,比如根据总线地址可以定位对应的设备目录等
Linux 内核驱动可以都是遵循一个逐层抽象的架构:
最上层的抽象层便于系统软件的访问,
中间层的实现硬件协议细节,同时提供上下两层连接的接口,
对于最下层的 driver 来说就是要定义底层驱动要实现的接口和实际的设备控制
由于 Linux 内核各类驱动的框架支持,driver 可以更加关注设备本身的特性
5. Android系统是基于Linux内核的,传统的Linux驱动开发和Android驱动开发有什么区别?
传统的 Linux 内核驱动开发,只需要定义好 Linux 需要的驱动接口就好了,之后
专注与设备本身的特性
而 Android 驱动开发,需要将设备操作接口继续封装,提供上层使用的 Java API;
driver 部分可以使用 C 代码,一些更复杂的操作可以用 Java 实现,然后 driver
提供对复杂操作的接口支持
一个很重要的方面是功能的分离定义,比如哪些是 driver 实现的,哪些是上层实现的,
它们之间的接口是怎样的,定义好这些,就可以按照传统 driver 的实现方式来做了;
从某种程度上,可以看作是传统 driver 跟相关 tools 转换成了 Android driver
和上层 Java 接口