Linux内核是个很庞大的东西,要全部了解清楚是很难的,所以我们所做的内核上的开发也是为驱动做一些基本的准备。
一、内核模块的特点
a) 不会被编译进内核之中,这样的话可以尽可能的控制Linux内核的大小。
b) 在内核运行期间,可以动态的进行加载或卸载。
二、相关函数的介绍
a) module_init宏表示在加载模块的时候,自动调用这个宏所指示的函数。
b) module_exit宏表示在卸载模块的时候,自动调用这个宏所指示的函数。
例如:
代码中:
module_init(funa);
module_exit(funb);
当加载该模块时,自动调用funa函数,同理当卸载该模块时,自动调用funb函数。
三、模块的相关命令
a) 加载模块:insmod modprobe
b) 卸载模块:rmmod
c) 查看模块:lsmod
insmod和modprobe的区别:
当a模块与b模块有依赖关系时,假设安装b模块需先安装a模块。
如果用insmod命令那么需要先insmod a.ko 然后再insmod b.ko。
如果用modprobe命令那么直接可以modprobeb.ko。
其中/lib/modules/“内核版本号”/modules.dep中记录了模块之间的依赖关系。
rmmod命令:只需要调用模块名即可,比如安装的时候是inmod a.ko 安装完毕后该模块存在于内核中的名字为a,那么卸载该模块只需要rmmod a即可。
前面讲了内核模块怎么安装和卸载,那么现在就正式讲解一下内核模块怎么编写。
以一个在内核中打印出”helloworld”到终端的例子讲解。
1、 首先要写模块加载时的入口函数和模块卸载时的入口函数
static int hello_init()
{
printk(“<0>hello world\n”);
return 0;
}
static void hello_exit()
{
printk(“<0>exit\n”);
}
module_init(hello_init);
module_exit(hello_exit);
这几段代码就是一个模块函数代码了,其中有几个需要注意:
a) static是为了防止函数命名污染。
b) 在内核中的打印函数是printk,其中<0>表示该信息的等级,数字越小,级别越高。
c) 用module_init和module_exit宏来指定入口函数。
当在命令行安装该模块时,会自动调用hello_init这个函数,当卸载该函数时,对自动调用hello_exit函数。
2、 模块可选信息
模块有一些用来表示相关信息的宏:
MODULE_LICENSE 用来告诉内核遵循什么协议 GPL GPLv2等
MODULE_AUTHOR 作者
MODULE_DESCRIPTION 描述
MODULE_VERSION 版本
MODULE_ALIAS 别名
例如:
MODULE_LICENSE("GPL");
MODULE_AUTHOR("yangboyuan");
MODULE_DESCRIPTION("KernelLink Module");
MODULE_ALIAS("asimplestmodule");
内核模块的初始化函数,撤消函数通常是这样的形式:
static int __init xxx-init(.....); //函数没有参数就写void,init前是两个_
staticvoid __exit xxx-exit(....);
其中__init属性标志, 表明该段代码位于代码段的子段初始化段,初始化段的程序只执行一次,执行完后它所占用的内存空间将被回收。当模块加载时,__init属性的函数就被执行;
__exit修饰词标记函数只在模块卸载时使用,如果模块被直接编进内核则该函数就不会被调用。如果内核编译时没有包含该模块,则此标记的函数将被简单地丢弃。
3、 Makefile文件的编写
大家都知道make命令,所以Makefile文件的作用在这里就没必要做过多介绍,我们先看看内核模块的Makefile怎么写的:
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
else
KDIR := /lib/modules/2.6.35.6-45.fc14.i686/build
all:
make-C $(KDIR)M=$(PWD) modules
clean:
rm-f *.ko *.o*.mod.o *.mod.c *.symvers
endif
其中:
a) KERNELRELEASE是在内核源码的顶层Makefile中定义的一个变量。
ifneq($(KERNELRELEASE),) 判断该变量是否为空。
在第一次读取执行此Makefile时,KERNELRELEASE没有被定义,
所以make将读取执行else之后的内容。
b) KDIR := /lib/modules/2.6.35.6-45.fc14.i686/build 是给KDIR这个变量赋值,值为当前linux运行的内核源码。2.6.35.6-45.fc14.i686是我的Linux下的,可以直接
KDIR :=/lib/modules/ $(shell uname -r) /build 这样写。
c) 当make的目标为all时,-C$(KDIR) 指明跳转到内核源码目录下读取那里的Makefile;M=$(PWD)表明然后返回到当前目录继续读入、执行当前的Makefile。当从内核源码目录返回时,KERNELRELEASE已被被定义,kbuild也被启动去解析kbuild语法的语句,make将继续读取else之前的内容。
d) 我们可以把上述的Makefile文件作为一个模板,只需要改动obj-m :=hello.o这条语句就可以了:obj-m=XXX.o。
编写ARM上的Makefile只需要对上面的Makefile作简单的修改即可
ifneq($(KERNELRELEASE),)
obj-m := hello.o
else
KDIR := /home/liye/forlinux/linux-2.6.36
all:
make-C $(KDIR)M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-
clean:
rm-f *.ko *.o*.mod.o *.mod.c *.symvers
endif
4、Makefile文件编写好了之后,在内核模块代码的当前目录下敲命令:”make”