Compiling and Loading 编译和加载
本节阐述更多的细节,如何把源代码变成内核中执行的子系统。
1. Compiling Modules
编译模块
首先,我们看看模块如何必须被建立,模块的建立过程区别于用户空间的应用程序;内核是一个大型的、独立的程序,它对于组成的各个部分都有详细的、明确的要求。模块建立过程也区别于旧版本的内核;新的建立过程对于用户来说更加简单,结果正确,但是他看起来和以前很不一样。如果想要深入了解这些问题,最好阅读Documentation/kbuild目录下的文件。
在我们建立内核模块之前,要了解一些先决条件。首先要有新的编译器,模块工具和其他的一些工具。Documentation/Changes子目录列出了一些工具版本;在建立内核之前,应该首先产看这些列表;用错误版本的工具会导致一些问题。有时候,新版本和老版本一样会出问题;如果你没有内核树,或者还没有编译内核,现在就开始吧。如果在你的文件系统中没有2.6版本的内核树,则无法建立可加载模块。
一旦建立好这些,为你的模块创建一个makefile文件非常的直接。对于本章的Hello,world来说,简单的一行就可以:
熟悉makefile、不熟悉2.6系统内核建立的读者,会有疑惑,这个makefile如何工作?上面的这一行不是传统的makefile。答案就是:内核建立系统处理余下的事情。上面的语句利用了扩展的GNU make语法,表示有一个内核模块使用目标文件hello.o建立。针对这个目标文件建立的模块最终被命名为hello.ko.
如果有一个模块文件module.ko需要两个源文件来建立,正确的书写:
makefile文件表明如何去工作,它必须在更大的内核建立系统中被调用。如果内核源码树在~/kernel-2.6目录下,makefile文件中要有这样的代码:
这个命令首先通过-C选项改变目录,也就是内核源码树目录。在这个目录里有内核的顶层makefile文件。M=选项使得makefile回到之前的模块代码目录。最后,还需要引用在obj -m变量中的模块,这个例子中是module.o.
但是,这样书写makefile文件比较繁琐,内核开发人员开发了一种makefile方式,使得在内核树之外书写makefile文件变的简单,轻松。
这里使用了扩展GNU make语法,这个makefile文件一般要读两次。当makefile文件被命令行调用时,它意识到KERNELRELEASE变量没有定义.它会定位到内核代码目录,利用建立在安装模块中的符号链接指回内核树。如果你没有运行你自己建立的内核,可以通过KERNELDIR=选项,设置KERNELDIR环境变量。一旦内核源码树找到,makefile文件调用default,make第二次调用。第二次读时,makefile文件设置obj-m,而且内核的makefile文件完成实际的模块建立工作。
上面的makefile是一个不完整的,它应该包括清除不需要的文件,安装内核等等。
2. Loading and Unloading Modules
加载和卸载模块
模块建立好后,就要把它加载进内核,insmod为我们做这个工作。insmod加载模块代码和数据,紧接着,执行类似ld的函数,把模块中没有解决的符号链接到内核的符号表。但是又不像连接器,内核不会改变模块的磁盘文件,而仅仅是在内存中的一个拷贝。insmod接受许多命令行选项,在模块链接到内核之前,能够指派参数。
因而,如果如果模块设计正确,它可以在加载时被配置;加载时的配置(load-time
configuration)比编译时配置(compile-time configuration)给了用户更多的灵活性。
insmod依赖于定义在kernel/module.c中的一个系统调用。sys_init_module函数给模
块分配内存空间;拷贝模块代码到内存区域,通过内核符号表解决模块中的引用,调用模块初始化函数。
在内核源代码中,系统调用都有sys_前缀,而其他函数是没有的,这样我们在源代码中查找系统调用会很方便。
modprobe和insmod一样用来加载内核。他的不同之处在于,加载模块时查看模块中是否引用了当前内核没有定义的符号,如果有,modprobe在定义相关符号的当前模块搜索路径中寻找其他模块,找到后,加载到内核。这种情况下,insmod会失败,并在系统日志中留下:unresolved symbols。
可以使用rmmod卸载模块,当时如果内核认为模块在使用或者内核配置成禁止模块卸载,那么rmmod不会成功。可以配置内核允许强制退出模块。
lsmod列出已经被加载入内核的模块列表,也会提示一些其他信息,比如某一模块使用了一个特殊模块等。lsmod读取/proc/modules虚拟文件。当前加载的模块信息也可以在/sys/module的sysfs虚拟文件系统中找到。
3. Version Dependency
版本依赖
内核模块代码需要重新编译到不同的链接内核。模块要关联某一特殊的内核中的数据结构和函数原型。内核不会假定给定的模块是针对正确版本而建立的。建立模块时会连接模块到当前内核书的vermagic.o文件。这个文件包含了模块要建立的那个内核的很多信息,包括他的目标内核版本、编译器版本和一系列非常重要的配置变量。加载模块时,这个信息需要和运行中的内核信息比较,若不匹配,则不能加载。
查看系统日志文件/var/log/messages,可以看到具体的问题。
如果针对某一个特定版本的内核编译模块,需要使用到源码树。在前面的例子中,更改makefile文件中的KERNELDIR变量就可以了。