1.1 我们得到一个内核镜像后,想要增加功能时,两种方式:
(1)一种方式是在配置选项中添加模块,重新编译内核,这样会很麻烦。
(2)找一个默认配置添加了全部功能的内核,这样内核太大了。
1.2 有没有一种机制使得编译出的内核本身并不需要包含所有功能,而在这些功能需要被使用的时候,其对应的代码被动态地加载到内核中呢?
答:Linux提供了这样的一种机制,这种机制被称为模块(Module)。模块具有这样的特点:
(1) 模块本身不被编译入内核映像,这控制了内核的大小。
(2) 模块一旦被加载,它就和内核中的其它部分完全一样。
2.1.1 当通过insmod或modprobe命令加载内核模块时,模块的加载函数会自动被内核执行,完成本模块的相关初始化工作。
(1)modprobe命令比insmod命令要强大,它在加载某模块时,会同时加载该模块所依赖的其它模块。使用modprobe命令加载的模块若以“modprobe -r filename”的方式卸载将同时卸载其依赖的模块。
(2) 使用modinfo <模块名>命令可以获得模块的信息,包括模块作者、模块的说明、模块所支持的参数以及vermagic:
[root@localhost driver_study]# modinfo hello.ko
filename: 全路径/ hello.ko
license: Dual BSD/GPL
author: 作者
description: 描述
alias: a simplest module
vermagic: 2.6.15.5 686 gcc-3.2 版本号 内核版本和模块版本必须相同才能安装
depends: 依赖(这个模块是否依赖其他模块,kconfig中指定)
(3)Linux内核模块加载函数一般用static 关键字声明为内部链接,__init,本质上是个宏定义,在内核源代码中就有#define __init xxxx。这个__init的作用就是将被他修饰的函数放入.init.text段中去(本来默认情况下函数是被放入.text段中)。整个内核中的所有的这类函数都会被链接器链接放入.init.text段中,所以所有的内核模块的__init修饰的函数其实是被统一放在一起的。内核启动时统一会加载.init.text段中的这些模块安装函数,加载完后就会把这个段给释放掉以节省内存。在驱动,内核中出现__init,应用代码不会有。
解释:init段、静态、动态方式加载模块?
代码段: 本来函数是被放在.text段,使用__init后,把我们放在了init.text段,它在内核的init.h中有定义
静 态:就是menuconfig配置功能时,添加进去,再编译进内核,静态加载
动 态:就是以模块的形式动态加载,比如在rootfs命令行下 insmod
(3)模块加载函数必须以module_init(函数名) 的形式被指定。它返回整型值,若初始化成功,返回0。初始化失败时,应该返回错误编码。内核的错误码是一个负数,在中定义,形如ENODEV等。
(4)代码如下:
1. static void __exit foo_exit(void)
2. {
3. //...
4. }
5. module_exit(foo_exit);
2.2 模块卸载函数(必须)
(1)当通过rmmod命令卸载某模块时,模块的卸载函数会自动被内核执行,完成与模块加载函数相反的功能。
(2)Linux内核模块卸载函数一般用static 关键字声明为内部链接,并以__exit 标识。和__init 一样, __exit 也可以使对应函数在运行完成后自动回收内存。具体可以查看内核代码中__init 和__exit 这两个宏的定义。
(3)模块卸载函数必须以module_exit(函数名) 的形式指定,不返回任何值。
示例代码如下:
1. static void __exit foo_exit(void)
2. {
3. //...
4. }
5. module_exit(foo_exit);
(1)模块许可证(LICENSE)声明描述内核模块的许可权限,如果不声明 LICENSE,模块被加载时,将收到内核被污染(kernel tainted)的警告,因为内核按照LICENSE标准定义,你写的模块也必须标注相同LICENSE,内核才让你使用。在Linux2.6内核中,可接受的 LICENSE包括“GPL”,“GPL v2”,“GPL and additonal rights”,“Dual BSD/GPL”,“Dual MPL/GPL”和“Proprietary”。
(2)Linux2.6内核模块最常见的是声明代码如下(3.4内核用的GPL v2):
MODULE_LICENSE("Dual BSD/GPL");
(1)vermagic: 2.6.15.5 686 gcc-3.2 版本号 内核版本和模块版本必须相同才能安装,比如我们在模块的makefile中指定KERN_DIR = /root/driver/kernel时,这个内核版本与我们的模块默认的内核版本不匹配,安装会出现格式错误。
详细细节请看下面博主: