Hello World 模块
下面代码展示一个“hello world” 模块:
#include<linux/init.h>
#include<linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void)
{
printk(KERN_ALERT "hello, world\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "Goodbye, curel world\n");
}
module_init(hello_init);
module_exit(hello_exit);
module_init 和 module_exit使用了内核的特殊宏来表示上述两个函数所扮演的角色,MODULE_LICENSE 用来告诉内核该模块使用自由许可证。
编译模块
对上述的实例模块只需要下面一行就可以了:
obj-m := hello.o
上面的赋值语句说明了有一个模块需要从目标文件hello.o中构造,而构造的模块名称为hello.ko。
若我们要构造的模块名称为module.ko,并有两个源文件生成,则正确的makefile如下编写:
obj-m := module.o
module-objs := file1.o file2.o
为了让makefile正常工作,必须在内核构造系统环境中调用它们,若内核代码树保存在~/kernel目录中,则make命令是:
make -C ~/kernel M=‘pwd’ modules
上述命令首先改变目录到-C指定的位置(内核源代码目录),其中有保存内核的顶层makefile文件,M=让改makefile在构造modules目标之前返回到模块源码目录,然后modules目标指向obj-m变量中设定的模块。
模块makefile模板:
#如果已经定义了KERNELRELEASE,则说明是从内核构造系统调用
ifneq ($(KERNELRELEASE),)
obj-m := simple.o
#否则,是直接从命令行调用的
#这时要调用内核构造系统
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
装载和卸载模块
insmod装载模块,它实际上依赖定义在/kernel/module.c的系统调用,函数sys_init_module给模块分配内核内存以便装载模块。
modprobe也也用了装载模块,和insmod不同在于,它会考虑模块是否引用了当前内核不存在的符号,如果有,nodprobe会在当前模块搜索路径中查找定义了这些符号的模块,如果找到了这些模块,它会同时将这些模块装载到内核。在这种情况使用insmod,会装载失败。
rmmod从内核移除模块。
lsmod列出装载到内核的所有模块。
内核符号表
insmod使用公共内核符号表来解析模块中未定义的符号。
如果一个模块需要向其他模块导出符号:
EXPORT_SYMBOL(name);
EXPORT_SYMBOL_GPL(name);
快速参考
__init
__initdata
__exit
__exitdata
仅用于模块初始化或清除阶段的函数和数据标记。
#include<linux/sched.h>
包含大部分内核API定义
struct task_struct *current;
当前进程
current -> pid;
current -> comm;
当前进程ID和命令名
#include<linux/moduleparam.h>
module_param(variable, type, perm);
用来创建模块参数的宏,用户可在装载模块时调整这些参数的值。
static char *whom = "world";
static int howmany = 1;
module_param(howmany, int, S_IRUGO);
module_param(whom, charp, S_IRUGO);
最后一个参数是成员访问许可值,我们应使用<linux/stat.h>中存在的定义。如果perm为0,则不会有对应sysfs入口项,否则模块参数会在/sys/module中出现。S_IRUGO,表示任何人均可读取改参数。
insmod hellop howmany=10 whom=“Mom”