废话少说,开始我的第一个程序,同所有的程序一样,我先从Hello world开始:
先建一个test_modules目录,以便以后练习模块:
[root@hh hello] # pwd
/home/hh/test_modules/hello
[root@hh hello] # vim hello.c
#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,cruel world/n");
}
module_init(hello_init);
module_exit(hello_exit);
printk函数与printf类似,她可以看作是由内核导出并给模块使用的printf函数的内核版。她与printf基本相似,最大的不同点是她缺乏对浮点数的支持。
[root@hh hello]# vim Makefile
obj-m :=/lib/modules/2.6.30/build
PWD :=$(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
说明一点,刚开始写Makefile的时候可能不太懂Makefile的写法,上面的“$(MAKE)”前面不是几个空格,而是一个制表符的宽度(也就是按Tab键)。不然会报如下错误:
make: 没有什么可以做的为 `modules'
[root@hh hello]# make
make -C M=/home/hh/test_modules/hello modules
make: *** M=/home/hh/test_modules/hello: 没有那个文件或目录。 停止。
make: *** [modules] 错误 2
编译时出错了。检查hello.c没有发现错误,再看Makefile ,我晕,写的太急写错了,把Makefile的第一、二行改为:
obj-m :=hello.o
KERNELDIR :=/lib/modules/2.6.30/build
PWD :=$(shell pwd)
修改后再编译:
[root@hh hello]# make
make -C /lib/modules/2.6.30/build M=/home/hh/test_modules/hello modules
make[1]: Entering directory `/usr/src/kernels/linux-2.6.30'
CC [M] /home/hh/test_modules/hello/hello.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/hh/test_modules/hello/hello.mod.o
LD [M] /home/hh/test_modules/hello/hello.ko
make[1]: Leaving directory `/usr/src/kernels/linux-2.6.30'
哈哈,终于编译通过了,上次编了N遍都没通过,给我郁闷的呀。看看现在的hello目录:
[root@hh hello]# ll
总计 160
-rw-r--r--. 1 root root 293 06-03 20:34 hello.c
-rw-r--r--. 1 root root 67845 06-03 20:45 hello.ko
-rw-r--r--. 1 root root 497 06-03 20:45 hello.mod.c
-rw-rw-r--. 1 root root 34228 06-03 20:45 hello.mod.o
-rw-r--r--. 1 root root 34643 06-03 20:45 hello.o
-rw-r--r--. 1 root root 191 06-03 20:45 Makefile
-rw-r--r--. 1 root root 2171 06-03 20:45 Module.markers
-rw-r--r--. 1 root root 45 06-03 20:45 modules.order
-rw-r--r--. 1 root root 0 06-03 20:45 Module.symvers
[root@hh hello]# insmod ./hello.ko
insmod: error inserting './hello.ko': -1 Invalid module format
崩溃,怎么又装载不了啊???!!!查看message:
[root@hh hello]# vim /var/log/messages
Jun 3 21:14:26 hh kernel: hello: version magic '2.6.30 SMP mod_unload 686 'should be '2.6.30.10-105.2.23.fc11.i686.PAE SMP mod_unload 686
原来好像是因为内核树和本来的内核版本不匹配引起的。是构建内核树时失败了吗?当时检查没错呀,
[root@hh hello] #modinfo hello.ko
filename: hello.ko
license: Dual BSD/GPL
srcversion: 61C5C1D916FD9FEC63F1FD8
depends:
vermagic: 2.6.30 SMP mod_unload 686
[root@hh hello]# uname -r
2.6.30.10-105.2.23.fc11.i686.PAE
按照网上的帖子:http://www.linuxdiyf.com/viewarticle.php?id=19002:
内核拒绝加载模块,因为记载版本号的字符串不符(更确切的说是版本印戳)。版本印戳作为一个静态的字符串存在于内核模块中(vermagic:...)版本信息是在连接阶段从文件init/vermagic.o中获得的。这个问题我们可以借助选项--force-vermagic来解决,但这种方法存在潜在的危险。 另外一种解决方法是我们构建一个同我们预先编译好的内核完全相同的编译环境。
首先,准备同你目前的内核版本完全一致的内核代码树。然后,找到你的当前内核的编译配置文件。通常它可以在路径 /boot下找到,使用像config-2.6.x的文件名。你可以直接将它拷贝到内核代码树的路径下:
cp /boot/config-`uname -r` /usr/src/linux-`uname -r`/.config
让我们再次注意一下先前的错误信息:仔细看的话你会发现,即使使用完全相同的配置文件,版本印戳还是有细小的差异的,但这足以导致模块加载的失败。这其中的差异就是在模块中出现却不在内核中出现的custom字符串,是由某些发行版提供的修改过的makefile导致的。检查/usr/src/linux/Makefile,确保下面这些特定的版本信息同你使用的内核完全一致:
[hh@hh ~]$ cd /usr/src/kernels/linux-2.6.30/
[root@hh linux-2.6.30]# cp /boot/config-2.6.30.10-105.2.23.fc11.i686.PAE ./
[root@hh linux-2.6.30]# vim Makefile
VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 30
EXTRAVERSION =.10-105.2.23.fc11.i686.PAE
然后保存,再次,编译内核:
(1) 编译: [root@hh linux-2.6.30] # make
(2) 编译模块: [root@hh linux-2.6.30] # mak bzImage
(3) 编译模块: [root@hh linux-2.6.30] # mak modules
(4) 安装模块: [root@hh linux-2.6.30] # make modules_install
(5) 安装:[root@hh linux-2.6.30] # make install
[root@hh linux-2.6.30]# cd /home/hh/test_modules/
[root@hh test_modules]# cd hello/
[root@hh hello]# make
make -C /lib/modules/2.6.30/build M=/home/hh/test_modules/hello modules
make[1]: Entering directory `/usr/src/kernels/linux-2.6.30'
Building modules, stage 2.
MODPOST 1 modules
CC /home/hh/test_modules/hello/hello.mod.o
LD [M] /home/hh/test_modules/hello/hello.ko
[root@hh hello]# insmod ./hello.ko
哈哈,竟然没有报错,难道真的成功了?但是为什么没有显示Hello,world呢?看看message信息:
[root@hh hello]# vim /var/log/messages
Jun 4 10:42:55 hh kernel: Hello,world
[root@hh hello]# lsmod
Module Size Used by
hello 1140 0 [permanent]
如果看见这个信息,不要兴奋的跳起来哦!!!到目前为止,我终于自己写了一个模块,并装载成功了!
[root@hh hello]# rmmod hello
ERROR: Removing 'hello': Device or resource busy
郁闷啊,装载成功了,但是卸载不了,太打击人了。这个原因是因为在编译内核时没有配置卸载模块支持,得重新编译内核,晕倒!!!我都编了N遍内核了!那就编吧。但是修改内核配置选项,打开MODULE_FORCE_UNLOAD选项后 重新编译以后也还是不能够卸载,太不完美了,这个问题只好留下来,下次再解决了。
后记:
上面模块不能卸载的问题刚开始确实没能解决(打开MODULE_FORCE_UNLOAD选项后),但是后来再次rmmod的时候就又能卸载了,估计是因为重启电脑的原因吧。至今都不太明白,知道原因的朋友还望不吝赐教,但是现在装载的任何模块都可以卸载了 ^_^ ~~~。