一个 Linux 内核模块程序结构主要由以下几个部分组成:

  1. 模块加载函数(必须) 。
    当通过 insmod 或 modprobe 命令加载内核模块时,模块的加载函数会自动被内核执行,完成本模块的相关初始化工作。
  2. 模块卸载函数(必须) 。
    当通过 rmmod 命令卸载某模块时,模块的卸载函数会自动被内核执行,完成与模块加载函数相反的功能。
  3. 模块许可证声明(必须) 。
    模块许可证(LICENSE)声明描述内核模块的许可权限,如果不声明 LICENSE,模块被加载时,将收到内核被污染 (kernel tainted)的警告。大多数情况下,内核模块应遵循 GPL 兼容许可权。Linux 2.6 内核模块最常见的
    是以 MODULE_LICENSE( “Dual BSD/GPL” ) 语句声明模块采用 BSD/GPL 双LICENSE。
  4. 模块参数(可选) 。
    模块参数是模块被加载的时候可以被传递给它的值,它本身对应模块内部的全局变量。
  5. 模块导出符号(可选) 。
    内核模块可以导出符号(symbol,对应于函数或变量) ,这样其他模块可以使用本模块中的变量或函数。
  6. 模块作者等信息声明(可选) 。

模块加载函数

Linux 内核模块加载函数一般以_ _init 标识声明:

1 static int _ _init initialization_function(void)
2 {
3 /* 初始化代码 */
4 }
5 module_init(initialization_function);

在 Linux 2.6 内核中,可以使用 request_module(const char *fmt, …)函数加载内核
模块:

request_module(module_name);

request_module("char-major-%d-%d", MAJOR(dev), MINOR(dev));

模块卸载函数

Linux 内核模块卸载函数一般以_ _exit 标识声明:

1 static void _ _exit cleanup_function(void)
2 {
3 /* 释放代码 */
4 }
5 module_exit(cleanup_function);

通常来说,模块卸载函数要完成与模块加载函数相反的功能 :

  1. 若模块加载函数注册了 XXX,则模块卸载函数应该注销 XXX。
  2. 若模块加载函数动态申请了内存,则模块卸载函数应释放该内存。
  3. 若模块加载函数申请了硬件资源(中断、DMA 通道、I/O 端口和 I/O 内存等)
    的占用,则模块卸载函数应释放这些硬件资源。
  4. 若模块加载函数开启了硬件,则卸载函数中一般要关闭硬件。

模块参数

`module_param(参数名,参数类型,参数读/写权限)`,
  参数即模块中用到的全局变量,加载模块时可以通过
 `insmod(或 modprobe)模块名 参数名=参数值”` ,

向模块传递参数。如果不传递,参数将使用模块内定义的默认值。

导出符号

Linux 2.6 的“/proc/kallsyms”文件对应着内核符号表,它记录了符号以及符号所在的内存地址。
模块可以使用如下宏导出符号到内核符号表:

EXPORT_SYMBOL(符号名);
EXPORT_SYMBOL_GPL(符号名);

导 出 的 符 号(或者说函数) 将 可 以 被 其 他 模 块 使 用 , 使 用 前 声 明 一 下 即 可 。EXPORT_SYMBOL_GPL()只适用于包含 GPL 许可权的模块。

模块声明与描述

MODULE_AUTHOR(author);
MODULE_DESCRIPTION(description);
MODULE_VERSION(version_string);
MODULE_DEVICE_TABLE(table_info);
MODULE_ALIAS(alternate_name);

模块使用计数

int try_module_get(struct module *module);

该函数用于增加模块使用计数;若返回为 0,表示调用失败,希望使用的模块没有被加载或正在被卸载中。

void module_put(struct module *module);

该函数用于减少模块使用计数。

Linux 2.6 内核为不同类型的设备定义了 struct module *owner 域,用来指向管理此设备的模块。当开始使用某个设备时,内核使用 try_module_get(dev->owner)去增加管理此设备的 owner 模块的使用计数;当不再使用此设备时,内核使用module_put(dev->owner)减少对管理此设备的 owner 模块的使用计数。这样,当设备在使用时,管理此设备的模块将不能被卸载。只有当设备不再被使用时,模块才允许被
卸载。