第1章 内核模块
模块最大的好处是可以动态扩展应用程序的功能而无须重新编译链接生成一个新的应用程序映像,这种广义上的模块概念其实并非Linux系统所特有,在微软的Windows系统上动态链接库DLL(Dynamic Link Library)便是模块概念的一个典型应用场景,对应到Linux系统上这种模块以所谓的共享库so(shared object)文件的形式存在。
本章要讨论的主题——Linux内核模块,在概念及原理方面与上面提到的DLL和so模块类似,但又有其独特的一面,内核模块可以在系统运行期间动态扩展系统功能而无须重新启动系统,更无须为这些新增的功能重新编译一个新的系统内核映像。内核模块的这个特性为内核开发者开发验证新的功能提供了极大的便利,因为像Linux这么庞大的系统,编译一个新内核并重新启动将浪费开发者大量的时间。
虽然设备驱动程序并不一定要以内核模块的形式存在,并且内核模块也不一定就代表着一个设备驱动程序,但是内核模块的这种特性似乎注定是为设备驱动程序而生。Linux系统下的设备驱动程序员在开发一个新的设备驱动的过程中,使用的最多的工具之一是insmod,这就是一个简单的向系统动态加载内核模块的命令。很难想象,如果没有insmod这样的机制,在Linux底下调试一个设备驱动会是怎样的一件让人痛苦抓狂的事情!笔者相信,任何一个在Linux上面有过实际的驱动程序开发经历的人都会有类似的感受。
Linux系统虽然为内核模块机制提供了完善的支持,使得其下的内核模块是如此强大,然而现实中事情往往并非如预想的那样一帆风顺,如果对其幕后的机制不甚了解,在实际的开发过程之中,除了驱动程序自身要实现的功能可能会遇到麻烦以外,在利用Linux中的内核模块机制时,也会遇到各种各样的问题,比如在用insmod命令加载一个模块时,就很可能会碰到类似下面的错误信息:
root@AMDLinuxFGL:/# insmod demodev.ko
insmod: error inserting 'demodev.ko': -1 Invalid module format
如果dmesg一下,就会看到内核针对上述错误打印出的出错信息如下所示:
demodev: version magic '2.6.39 SMP mod_unload 586' should be '2.6.39 SMP mod_unload
modversions586'
直觉上,这应该不是在驱动程序自身要实现的功能上出现了问题,问题应该出在驱动程序所在的模块在加载时与系统中内核模块框架互动的环节中。很明显,Linux内核设计中为模块这种机制提供了完善的支持,以内核模块形式存在的设备驱动程序也必然要遵循这种框架下的规则才能正常工作,也许绝大多数情况下模块都会工作得很好,然而诸如上面提到的这类模块相关的错误也绝非罕见。
既然规则不由我们定义,那么了解并遵守规则就成了避免或者解决这类问题的唯一途径。一个成熟的Linux设备驱动程序开发者应该能很快确定这些错误的原因并给出相应的解决方案,而新手在这类错误面前更多的感觉则可能是迷惘和不知所措。因此,无论是出于现实工作的需要,还是为了满足自己的好奇心,Linux下的设备驱动程序员都有必要花上足够多的时间来了解隐藏在内核模块背后的技术细节,而这也正是本章要深入探讨内核模块机制的目的。本章将重点关注并讨论如下的问题:
● 模块的加载过程。
● 模块如何引用内核或者其他模块中的函数与变量。
● 模块本身导出的函数与变量如何被别的内核模块所使用。
● 模块的参数传递机制。
● 模块之间的依赖关系。
● 模块中的版本控制机制。