Linux的内核模块机制允许开发者动态的向内核添加功能,我们常见的文件系统、驱动程序等都可以通过模块的方式添加到内核而无需对内核重新编译,这在很大程度上减少了操作的复杂度。模块机制使内核预编译时不必包含很多无关功能,把内核做到最精简,后期可以根据需要进行添加。
而针对驱动程序,因为涉及到具体的硬件,很难使通用的,且其中可能包含了各个厂商的私密接口,厂商几乎不会允许开发者把源代码公开,这就和linux的许可相悖,模块机制很好的解决了这个冲突,允许驱动程序后期进行添加而不合并到内核。OK,下面结合源代码讨论下模块机制的实现。
内核模块加载函数一般以_ _init 标识声明:static int _ _init initialization_function(void) 将它放在内核入口:module_init(initialization_function);
内核模块卸载函数一般以_ _exit 标识声明,static void _ _exit cleanup_function(void); 将它放在模块出口,module_exit(cleanup_function);;
下面对上述函数实例验证: demo.c
#include <linux/init.h> #include <linux/module.h> int a=10; module_param(a,int,0644); MODULE_PARM_DESC(a,"this is int val"); short b=14; module_param(b,short,0771); MODULE_PARM_DESC(b,"this is short val"); char *p = "hello kernel"; module_param(p,charp,0664); MODULE_PARM_DESC(p,"this is string val"); int tt[5]; int num; module_param_array(tt,int,&num,0664); MODULE_PARM_DESC(tt,"this is array"); int pp=500; module_param_named(ww,pp,int,0664); MODULE_PARM_DESC(ww,"this is array"); char buf[10]; module_param_string(buf,buf,10,0664); MODULE_PARM_DESC(buf,"this is char array"); static int __init demo_init(void) { int i; printk("a = %d\n",a); printk("b = %d\n",b); printk("p = %s\n",p); printk("pp = %d\n",pp); printk("buf = %s\n",buf); for(i=0; i<num; i++){ printk("tt[%d] = %d\n",i,tt[i]); } printk("%s:%s:%d\n",__FILE__,__func__,__LINE__); return 0; } static void __exit demo_exit(void) { printk("%s:%s:%d\n",__FILE__,__func__,__LINE__); } module_init(demo_init); module_exit(demo_exit); MODULE_LICENSE("GPL");makefile
KERNELDIR := /lib/modules/$(shell uname -r)/build/ #KERNELDIR := /home/linux/kernel/kernel-3.4.39/ PWD := $(shell pwd) all: make -C $(KERNELDIR) M=$(PWD) modules clean: make -C $(KERNELDIR) M=$(PWD) clean obj-m := demo.o编译及加载: a. 我们先不加任何参数加载驱动:insmod demo.o,dmesg查看打印如下: b. 我们加入参数: //传递字符串是不能有空格 dmesg查看打印如下: 这里我们没有对PP变量该名称,没找到怎么修改,大家可以留言。 当权限不为0的时候,模块加载后会生成变量对应的文件,文件内容即变量值
声明:MODULE_AUTHOR 作者 MODULE_DESCRIPTION 描述 MODULE_ VERSION 版本 MODULE_DEVICE_TABLE 设备表 MODULE_ALIAS 别名