1、内核模块注册注销:
块加载函数有返回值,模块卸载函数无返回值。两者都是无参函数,加载函数用_ init修饰,卸载函数用 _exit修饰。
#define __init __attribute__((__section__(".init.text")))
#define __exit __atrribute__((__section__(".exit,text")))
static int _ _init demo_init(void);
static void _ _exit demo_exit(void);
## 2、模块参数 ##
模块参数是可以从命令行传给模块的供用户在加载模块时对模块中的参数值进行更改。可以作为内核模块参数导出的类型有byte、int、uinit、short、ushort、long、ulong、charp、bool、invbool。在/sys/module/paremters下会有以参数命名的文件节点。
static int i=0;
module_param( i , int , 0644 );
static int arr[6] = {0};
int len = 0;
module_param_array( arr , int , &len , 0664 );
static char* name="Hello";
module_param( name , charp , 0664 );
//当参数为字符串时,在导出参数是需要指定字符串的内部名字和外部名字。
static char innane="Hello";
module_param_string( outname , inname , sizeof( inname ) , 0664);
//总结:
a、导出参数为简单类型时,直接用module_param(参数名,类型,权限)
b、导出参数为整型数组时,参数要多一个描述数组长度的变量的地址,而且导出参数的函数的名字也多了个array:module_param_array(数组名,int,&int,0664);
c、导出参数为字符串数组时函数名称改变为module_param_string,参数中多了一个字符串的外部名字:
module_param_string(outname,inname,sizeof(inname),0664);
3、内核符号:
内核的符号在/proc/kallsyms文件中可以看到内核符号以及该符号对应的内存地址。内核模块可以导出自己的符号表以供给其它模块使用:
EXPORT_SYMBLE(符号名);
EXPROT_SYMBOL_GPL(符号名);
//符号名一般为函数名
4、内核模块的编译:
要正确的编译一个我们自己的内核模块其实并不简单,首先我们要准备一份内核源码,这个内核源码是我们的模块将要加载的地方。其次要保证这个内核源码是被编译过的。然后我们来写Makefile:
ifneq ($(KERNELRELEASE) , )
mymodule-objs = xxx.o
obj-m =mymodule.o
else
KEIELDIR := /lib/modules/$(shell uname -r)/build
PWD :=$(shell pwd)
modules:
make -C $(KERNELDIR) M=$(PWD) modules
endif
clean:
rm -rf .tmp_versions Module.symvers modules.order .tmp_versions .*.cmd *.o *.ko *.mod.c
//书写Makefile注意事项:1、在需要修改模块的名字时使用我们当前的这个形式但是需要注意的是,“ -objs ”是与我们的“ .c ”源码所对应的;“ -obj ”是与我们模块的新名字所对应的。
内核模块Makefile分析:
首先在自己的模块中执行make之后,则进入我们自己写的Makefile中,首先判断
(KERNELRELEASE)是否为空,由于
(KERNELRELEASE)是在Linux内核源码顶层目录下定义的,所以此时这个变量是为空的。直接执行else下面的语句:KERNELDIR=Linux内核源码顶层Makefile的目录,PWD=当前Makefile所在目录。接下来看到“ modules: ”这个伪目标,此时它下面的命令无条件执行:make -C
(KERNELDIR)表示进入Linux源码目录下执行它的顶层Makefie,M=
(PWD)表示执行完Linux顶级目录下的Makefile之后,再回到
(PWD)目录下执行该目录下的Makefile。由于此时
(PWD)代表我们自己模块的Makefile的路径,所以在执行完Linux源码下的目录之后第二次回到的我们自己的Makefile,不同的是此时的$(KERNELRELEASE)是有值的,它代表内核发布版本信息。所以ifneq条件是成立的,继续执行下面的语句。直到此时为止,才是真正的开始编译我们自己写的模块。