基本概念
(1)模块本身不被编译入内核映像,从而控制了内核镜像的大小。模块一旦insmod,它就和内核中的其他部分完全一样
(2)内核中已加载模块的信息也存在于/sys/module目录下;内核中将包含/sys/module/test_mod目录
(3)modprobe在加载某模块时,会同时加载该模块所依赖的其他模块。使用modprobe命令加载的模块若以“modprobe -r filename”的方式卸载,将同时卸载其依赖的模块
加载函数
(1)Linux内核模块加载函数一般以__init标识声明,它返回整型值;若初始化成功,应返回0。而在初始化失败时,应该返回错误编码
(2)在Linux中,所有标识为__init的函数如果直接编译进入内核,成为内核镜像的一部分,在连接的时候都会放在.init.text这个区段内--#define _ _init _ _attribute_ _ ((_ _section_ _ (".init.text")))
(3)所有的__init函数在区段.initcall.init中还保存了一份函数指针,在初始化时内核会通过这些函数指针调用这些__init函数,并在初始化完成后,释放init区段(包括.init.text、.initcall.init等)的内存
(4)数据也可以被定义为__initdata,对于只是初始化阶段需要的数据,内核在初始化完后,也可以释放它们占用的内存
(5)在Linux内核中,可以使用request_module(const char*fmt,…)函数加载内核模块
static int hello_data __initdata = 1;
static int __init hello_init(void)
{
printk(KERN_INFO "Hello, world %d\n", hello_data);
return 0;
}
module_init(hello_init);
卸载函数
Linux内核模块加载函数一般以__exit标识声明,在模块卸载的时候执行,而不返回任何值;只是退出阶段采用的数据也可以用__exitdata来形容
static void _ _exit hello_exit(void)
{
/* 释放代码 */
}
module_exit(hello_exit);
模块参数
(1)可以用“module_param(参数名,参数类型,参数读/写权限)”为模块定义一个参数,参数类型可以是byte、short、ushort、int、uint、long、ulong、charp(字符指针)
(2)也可以拥有参数数组,形式为“module_param_array(数组名,数组类型,数组长,参数读/写权限,使用逗号分隔输入的数组元素;在/sys目录下,也可以看到某模块的参数
(3)在装载内核模块时,用户可以向模块传递参数,形式为“insmode(或modprobe)模块名参数名=参数值”,如果不传递,参数将使用模块内定义的缺省值
(4)如果模块被内置,无法insmod了,bootloader可以通过在bootargs里设置“模块名.参数名=值”的形式给该内置的模块传递参数
static char *pig_name = "Peppa";
module_param(pif_name, charp, S_IRUGO);
static int pig_age = 4000;
module_param(pig_age, int, S_IRUGO);
导出符号
Linux的“/proc/kallsyms”文件对应着内核符号表,它记录了符号以及符号所在的内存地址;经过下面接口导出到/proc/kallsyms的符号就可以被其他模块使用
EXPORT_SYMBOL(sym_name);
EXPORT_SYMBOL_GPL(sym_name);
模块声明
可以用MODULE_AUTHOR、MODULE_DESCRIPTION、MODULE_VERSION、MODULE_DEVICE_TABLE、MODULE_ALIAS分别声明模块的作者、描述、版本、设备表和别名
MODULE_AUTHOR(author);
MODULE_DESCRIPTION(description);
MODULE_VERSION(version_string);
MODULE_DEVICE_TABLE(table_info);
MODULE_ALIAS(alternate_name);
模块示例
示例代码
#include <linux/module.h>
#include <linux/kernel.h>
static int __init test_mod_init(void){
printk("test_mod init!\n");
return 0;
}
static void __exit test_mod_exit(void){
printk("test_mod exit\n");
}
module_init(test_mod_init);
module_exit(test_mod_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("LYT");
示例Makefile
export STAGING_DIR=交叉编译工具绝对路径
CROSS_COMPILE:= 交叉编译工具绝对路径/arm-openwrt-linux-muslgnueabi-
ARCH:= arm
#CC:= $(CROSS_COMPILE)gcc
#LD:= $(CROSS_COMPILE)ld
obj-m:=test_mod.o
KDIR:=内核根目录绝对路径
PWD:=$(shell pwd)
default:
$(MAKE) -C $(KDIR) M=$(PWD) modules ARCH=$(ARCH)
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
$(RM) Module.markers modules.order
$(RM) $(PWD)/src/modules/kmod/client/kmod/Module.markers
$(RM) $(PWD)/src/modules/kmod/client/kmod/modules.order