- 源文件
头文件+模块参数+模块功能函数+其他+模块加载函数+模块卸载函数+模块许可声明
#include <linux/init.h>
#include <linux/module.h>
#include <linux/>
#include "add_sub.h"
static long a=1; //模块参数
static int b=1;
long add_integer(int a, int b) //模块功能函数
{
return a+b;
}
long sub_integer(int a, int b)
{
return a-b;
}
static int hello_init(void) //模块加载函数,insmod时自动调用
{
printk(KERN_ALERT "Hello World\n");
return 0;
}
static void hello_exit(void) //模块卸载函数,rmmod时自动调用
{
printk("KERN_ALTER "Hello World Exit\n");
}
module_param(a, long, S_IRUGO); //module_param(参数名, 参数数据类型, 参数读写权限)
module_param(b, int, S_IRUGO); //导出参数没有浮点类型,内核并不能完美的支持浮点类型,printk()也不支持浮点类型
*+++++导出功能函数以供其他模块调用+++++*
*----------
1. 在insmod module.1时,内核发现以ELF格式组织的module.1的.symtab数据结构中有函数可以导出,内核便将其中导出的功能函数的内存地址记录在“内核符号表”;insmd module.2时,在以ELF格式组织的module.2的symtab中发现未解析的函数引用,通过查询“内核符号表”,将对应函数地址写入module.2的symtab中
2. 模块中定义的函数和内核中的函数冲突,怎么办?编译器认为模块中的函数都是私有的,除非使用导出宏
+++++++++++*
EXPORT_SYMBOL(add_integer); //导出宏
EXPORT_SYMBOL(sub_integer);
module_init(hello_init); //模块加载函数和卸载函数的宏定义
module_exit(hello_exit);
MODULE_LICENSE("Dual BSD/GPL"); //模块许可声明
- 不同版本的内核需要对应版本的编译工具(eg.gcc binutil)
- 编译的内核模块应该和测试系统对应同一内核源码
- 内核源码应该至少编译过一次
ifeq ($(KERNELRELEASE),) # 检查变量KERNELRELEASE是否为空
KERNELDIR ?= /linux-2.6.34.14/linux-2.6.34.14
PWD := $(shell pwd)
modules: # Makefile的一个功能选项
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules # -C $(KERNELDIR)使编译器进入内核源码目录,读取其Makefile,变量KERNELRELEASE将在这里被赋值(跟踪的makefile文件可知,类似于KERNELRELEASE的许多变量,KERNELVERSION..等等将在这里被赋值并导出)
# 编译器根据M=$(PWD)第二次进入模块所在目录,并再一次执行Makefile,此时KERNELRELEASE将是内核发布版本信息,
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions # 删除中间文件
else
obj-m := hello.o # 开始编译模块
endif
- insmod 模块.ko 参数1=value1 参数2=value2 #加载模块,加载后自动调用hello_init
- rmmod 模块.ko #卸载模块,自动调用hello_exit
- modprobe 可以解决模块之间依赖性的加载和删除模块命令
- lsmod 列出已经加载模块相关信息
- modinfo 查询模块作者、版权等信息