Linux驱动是驻留在内核空间的一个个模块,所以要学习内核驱动,必须对内核模块的编译与程序的框架有所了解。
以前在看内核模块编译方法的时候,曾经看到许多方法,但是基本来说可以分了两种,一是编写Makefile文件,另外是直接在gcc编译命令中加入__KERNEL__和__MODULE__宏来编译。但是使用命令来编译的时候一般都会出现很多错误,,,
这经常让人摸不着头脑,因为使用命令来编译是针对2.4内核的,而使用Makefile文件的方法一般是对于2.6内核的。
由于Linux内核从2.4到2.6改变了很多,而由固定内核版本编译的模块只能在该版本的内核中使用,并且两个版本的内核模块编译方法不一样,而网上有些文章没有说明是在哪个内核下面的模块,然后如果我们不用对应的内核版本的话就会出现很多莫名其妙的错误,,,
好,现在我们要说的是Linux2.6内核的模块~~~
内核模块可以直接编译链接到内核里面,也可以编译成独立的模块手机加载到内核空间里面,
对于集成系统的一般采用前者,在内核源码中添加相应的源码与Makefile,
但是对于我们这些大多数只是学习一下的人,一般都采用后者,编译好*.ko文件之后,使用insmod,rmmod来加载和卸载模块~
编译所需要的条件是必须安装编译工具链,就是gcc等工具,呵呵,这个要看你需要编译什么平台下面的,如果目标是PC机下面的内核,就是gcc,如果是嵌入式的内核则需要安装嵌入式的编译工具链比如:arm-eabi-gcc。
还需要的是需要有目标内核版本的源码,也就是使用2.6.x内核版本的源码编译的模块,有可能只能在2.6.x的内核中加载,,,
因为我曾经用2.6.36编译了一个模块,结果不能加载到2.6.32的内核上面。。。
我使用Ubuntu10.04在我的系统里面的源码位置在/lib/modules/2.6.32-21-generic/build里面。
有了这两个就可以编译内核模块了~~~
请看下面的源代码scull.c:
#include <linux/init.h> #include <linux/module.h> ///////////////////////////////////////////////// MODULE_LICENSE("Dual BSD/GPL"); ///////////////////////////////////////////////// ///////////////////////////////////////////////// static __init int scull_init(void) { printk(KERN_ALERT "scull init!/n"); return 0; } static __exit void scull_exit(void) { printk(KERN_ALERT "scull exit!/n"); } module_init(scull_init); module_exit(scull_exit);
在编写内核模块源码的时候必须包含的头文件:
#include <linux/init.h>
#include <linux/module.h>
而许可证,可以用下面的宏来定义,具体的含义可以看一下module.h文件
MODULE_LICENSE("Dual BSD/GPL");
定义模块加载到内核空间的时候执行的函数,scull_init,一般在这里做初始化的数据处理
module_init(scull_init);
定义卸载模块的时候,调用的函数,,scull_exit,一般在这里释放模块在运行的时候所占用的资源
module_exit(scull_exit);
Makefile文件:
#Makefile obj-m := scull.o PWD := $(shell pwd) KERN_DIR = /lib/modules/$(shell uname -r)/build default: $(MAKE) -C $(KERN_DIR) M=$(PWD) modules clean: $(MAKE) -C $(KERN_DIR) M=$(PWD) clean # rm -f *.o *.mod.c *.order *.symvers *.ko
obj-m := scull.o
这个表示需要编译的模块
PWD := $(shell pwd)
当前源码的位置
KERN_DIR = /lib/modules/$(shell uname -r)/build
得到内核源码的位置,因为编译内核模块需要包含内核源码里面最顶层的Makefile文件,得到里面的一些设置与定义才能正确编译
$(MAKE) -C $(KERN_DIR) M=$(PWD) modules
-C 表示进入到内核源码目录里面去,
M=$(PWD) 表示需要编译的子目录
在源码目录运行:make
然后可以得到内核模块文件:scull.ko
sudo insmod scull.ko
把模块加载到内核
sudo rmmod scull
把模块从内核空间中卸载
dmesg | tail
查看dmesg最后几行,可以看到内核模块printk的输出
cat /proc/modules | grep scull
或是lsmod
可以查看模块是否已经加载到内核之中了。