10. 4.1内核版本与模块版本的兼容性

可装入模块的编写者必须意识到,可装入模块既独立于内核又依赖于内核,所谓“独立”是因为它可以独立编译,所谓依赖是指它要调用内核或其他已装入模块的函数或变量,因此,内核版本的变化直接影响着曾经编写的模块是否能被新的内核认可。

例如,mydriver.o是基于Linux2.2.1内核编写和编译的,但是有人想把它装入到Linux2.2.2的内核中,如果mydriver.o所调用的内核函数在2.2.2中有所变化,那么内核怎么知道内核版本与模块所调用函数的版本不一致呢?

为了解决这个问题,可装入模块的开发者就决定给模块也编以内核的版本号。在上面的例子中,mydriver.o目标文件的.modinfo特殊区段就含有“2.2.1”,因为mydriver.o的编译使用了来自Linux 2.2.1的头文件,因此,当把该驱动程序装入到2.2.2内核时,insmod就会发现不匹配而失败,从而告诉你内核版本不匹配。

但是,Linux2.2.1 2.2.2之间的一点不兼容是否真的影响mydriver.o的执行呢?mydriver.o仅仅调用了内核中几个函数、访问了几个数据结构,可以肯定地说,这些函数和数据结构并不一定随每个版本的稍微变化而变化。这种版本上的严格限制在一定程度上带来了不便,因为每当有版本变动时,就要重新编译(或从网上下载)许多可安装模块,而这种变动也许并不影响模块的运行,因此应当有其他的办法来解决这个问题。

   办法之一,insmod有一个-f选项来强迫insmod忽略内核版本的不匹配,并把模块插入到任何版本中,但是,你仍然会得到一个版本不匹配的警告信息。

    办法之二,就是将版本信息编码进符号名中,例如将版本号作为符号名的后缀。这样如果符号名相同而版本号不一致,insmod就会认为是不同的符号而不予连接。但是,内核把是否将版本信息编码进符号名中是作为一个可选项CONFIG_MODVERSIONS来提供的。如果需要版本信息,就可以在编译内核代码前的系统配置阶段选择这个可选项;如果不需要,就可以在编译模块的源代码时加上-D CONFIG_MODVERSIONS取消这个选项。

   当以符号编码来编译内核或模块时,我们前面介绍的EXPORT_SYMBOL()宏定义的形式就有所不同,例如模块最常调用的内核函数register_chrdev(),其函数名的宏定义的在C中为:

#define register_chrdev register_chrdev_Rc8dc8350

把符号register_chrdev定义为register_chrdev加上一个后缀,这个后缀就是register_chrdev()函数实际源代码的校验和,只要函数的源代码改动一个字符,这个校验和也会发生变化。因此,尽管你在源代码中读到的函数名为register_chrdev,但C的预处理程序知道真正调用的是register_chrdev_Rc8dc8350