前言
在Linux庞大的源码树中,设备驱动程序部分的代码已经占了相当大的比例。现实的工作中,大量的采用Linux系统的平台需要设备驱动程序才能把Linux的内核真正运行起来,同时通过编写Linux设备驱动程序,使得我们经由亲手编写具有特权等级的代码来一探Linux内核幕后的秘密成为可能。所以,无论是从日常工作的需要还是只为单纯满足对Linux内核机制好奇心的角度来说,学习并掌握Linux设备驱动程序的编写都是非常必要的,同时也是一件非常有趣且有意义的事情。
初衷与定位
这本书并不仅仅是单纯地讨论如何在Linux系统下编写一个设备驱动程序,因为关于这方面的内容,市面上已经有大量类似的图书可供参考。本书的总体思想是从内核的角度来看设备驱动程序,从设备驱动程序的角度深入到内核中,比如通过对spin_lock以及spin_lock_irq等内核源码的分析,来告诉你在什么场合下应该使用spin_lock,什么场合下又应该选择spin_lock_irq。还有,比如我们几乎每天都会在设备驱动程序所代表的内核模块中使用MODULE_LICENSE("GPL")这样的声明,这个声明是如此地平凡,以至于我们常常忽略它存在的价值。但是在某个夜深人静的夜晚,感觉长夜漫漫无心睡眠时,在你内心深处的某个地方是否会想过,这个声明对一个内核模块而言,它到底意味着什么,如果没有它,加载这样一个模块对系统又会造成什么样的影响,如此等等,读者都可以在阅读本书的过程中找到答案。
很显然,只有当你清楚地理解了一个东西的内在机制,你才能更好地去使用它们,如果不幸在使用过程中出现问题,也才可以快速将其定位并最终予以解决。台湾著名技术作家侯捷曾引林雨堂先生在《朱门》中的一句话,“只用一样东西,不明白它的道理,实在不高明”,来描述他当时写作时的心境,其实这句话也同样适合我用来阐明写作本书的初衷之一。
但是这并不意味着只有Linux系统下设备驱动程序的编写老手才适合阅读本书,因为我在本书写作过程中,一般会先给出一个总体的框架,然后在此基础上对Linux提供给设备驱动程序使用的每一个常见而重要的内核设施进行细致地分析,同时辅之以验证性质的代码来使得这种略嫌抽象的讨论具体化,以激发读者对技术探索的兴趣。所以即便是入门级的读者,也可以通过阅读本书来加深对Linux下编写设备驱动程序的理解。
另外要说的是,读者不应该寄希望于阅读两三本书就可以掌握Linux下设备驱动程序编写的精髓,所有的书籍只能在大体上给你一个参考借鉴的作用,真正的理解还要靠读者自己去努力,诚所谓“纸上得来终觉浅,绝知此事要躬行”。
编排与范围
在本书的结构编排上,我努力使各章节独立起来,但是少量的向前或者向后引用还是必不可少的。但是总体上,我将最基本的篇章尽量放到前面,一些加强型或者高级点的话题尽量放到后边。在描述驱动程序内核机制方面,为了避免单纯的代码解释所带来的抽象感,我会使用具体的例子来将所能看到的驱动程序的前台行为和它的幕后机制串联起来,以帮助读者建立起全面立体的设备驱动程序架构蓝图。不过Linux对某些特性的支持因为考虑到各种平台和性能等诸多因素,其实现很可能会有多种不同的方法,比如从内核态驱动程序向用户空间导出信息的文件系统方面,就至少有proc和sysfs两种形式。因此本书在描述具体的例子时,一定是遵循其中的某种实现,在诸多实现机制的选择上,本书会从实用性和实时性角度出发,采用内核中最新引入或者是最有发展前景的实现,对于某些即将过时的实现机制(因为兼容或者代码维护工作量的关系,一些老的机制可能依然残留在新版的内核代码中),除非出于技术细节的对比或者从增加知识面的角度考虑会有所涉及,否则将不会作为本书的主线。
在代码的引用上,为了突出功能主线部分和削减本书的篇幅,我会删除代码中用来增加调试信息、性能增强及防御性代码这些部分。对于系统体系架构相关的代码,我主要以x86与ARM平台为主,因为这两者是当前最流行的两种处理器架构。关于本书所参考的Linux内核源码的版本,在本书刚开始写作时参考的是2.6.35的版本,在写作的中后期,已经将内核版本更新到了2.6.39,在本书的修订阶段,我已经努力将之前完成的内容更新到了2.6.39。当然,因为作者时间精力所限,加之Linux内核本身就博大精深,内核版本也一直在不断更新变化中,所以书中肯定还会有这样那样潜在的错误,希望读者朋友们能不吝批评指正,以使我们得以共同提高。
创作历程
我有幸自参加工作以来,在Linux下从事设备驱动程序相关的开发工作已经有9年多的时间,这期间在Linux上所接触的平台既有x86,也有ARM,甚至包括少量的PowerPC。在我看来,学习某一操作系统下的设备驱动程序的编写,主要包含两个方面:一个是该操作系统本身对设备驱动程序框架的支持,也可以称之为设备驱动模型,另一个则是对要驱动的硬件的理解。对于后者,设备驱动程序开发者将要面对各种各样的硬件设备,了解它们的最好也最直接的方法当然是这样硬件的datasheet。前者则主要和操作系统息息相关,比如在Linux系统下开发设备驱动程序,必然要熟练掌握Linux为设备驱动的编写所提供的各种内核实施及相关的各种数据结构,本书的内容主要就是探讨Linux内核为设备驱动程序编写所提供的所有这些设施的幕后技术。
本书最早的写作酝酿大约在2010年10月份前后,在此之前,或者是出于自己对以往积累的技术总结的需要,或者是出于将自己的一些技术心得与同行分享的目的,总之,我陆陆续续在一些论坛上发表了若干剖析Linux设备驱动程序内核机制的帖子,这些帖子最终使我萌发了用一本书来总结自己以往的Linux设备驱动程序开发经验的想法。我把最初的大约一章半的稿子发给了电子工业出版社,很快就得到了策划编辑张春雨先生的肯定,接下来也很顺利地通过了选题的论证,这之后就是一段极其漫长且非常辛苦的写作过程。时间是最大的挑战,由于白天需要工作,写作的时间只能是留给夜晚或者周末,在写作最紧张的时刻,经常要写到凌晨2点多。除了时间上的困难之外,如何将一个技术点用最透彻最简洁的语言描述清楚,如何对Linux内核中纷繁复杂的内容进行取舍,这些也都是非常耗费精力的事情。技术本身的理解也许并不困难,难在如何去把你心中掌握的东西清晰准确地以文字的方式表达出来,这不同于论坛的发帖,可以非常自由甚至随心所欲,写书的话,必须考虑它的完整性、逻辑性以及可读性,同时还要考虑将来潜在的读者群。尤其是如果你想认认真真写一本书的话,有时候甚至需要反复推敲一个技术点的表达方式。在写作灵感枯竭的时候,看着时间飞快掠过,而眼前的文档却没有留下几行字,那种强烈的挫折感与沮丧感真得会让人动摇自己的信念:自己是否还能坚持下去?!所以当这本书即将出版时,我还很有些恍惚,不敢相信自己居然磕磕绊绊地最终完成了这些书稿。
意见反馈
读者如果在阅读本书的过程中有任何意见或者建议,欢迎通过下面的E-mail与我取得联系:ricard_chen@yahoo.com。
关于本书使用到的源代码,读者可在www.embexperts.com网站上下载。另外,关于本书后续的一些勘误、某些技术细节方面的讨论也会在该网站相应的版面上进行。
致谢
首先,我要感谢我的家人,如前所述,写书占去了我大量的业余时间,我的父母和怀孕的妻子在此期间承担了几乎所有的家务劳动,替我捣腾出不少的写作时间,感谢她们!我的宝贝女儿在今年8月15日健康出生,成为我的家庭中新的一员,这本书也正好可以作为父亲的见面礼送给她——可爱的萌萌同学。
其次是电子工业出版社的张春雨与白涛编辑,从选题的论证到文字编辑,他们都付出了极其辛苦的劳动并且提出了很多有益的建议,那些逝去的不堪回首岁月里满眼尘封的E-mail见证了这一点!当然,还要感谢我现在所效力的AMD公司,因为它使得我不必为生活所迫去写一本书,对技术的热情与兴趣才是我最终得以坚持下来的最大因素。
最后,在本书的审核方面,AMD的PMTS及显卡驱动软件架构师俞辉在百忙中为本书作序并审核了部分章节,AMD上海研发中心Linux Graphic Base Driver团队的Lisa Wu及研发经理刘刚也为本书的写作提供了支持,诺基亚与西门子的研发经理胡兵全审核了本书第1章及第12章,EMC的PE Thomas审核了本书第3章及第4章。Marvell的资深软件工程师James Lai亦审核了本书部分章节并有宝贵意见,在此一并表示感谢!
陈学松
2011年8月29日于上海