一、设置系统。
我的系统环境:
系统:fedora12
内核:2.6.32.12-115.fc12.i686
把 kernel-headers kernel-devel装上。kernel-headers-2.6.32.12-115.fc12.i686 kernel-devel-2.6.32.12-115.fc12.i686
说明:书上说构建一个内核源代码树,我不明白到底是什么意思。我把kernel-headers kernel-devel装上后,编译了两个模块,发现都可以通过。其实编写内核模块只需要kernel-headers 和kernel-devel应该就足够了,这个是我自己理解的,不知道正确与否,如理解不对,请指正。
说白了,编写内核模块必须要有内核头文件(不以C头文件)以及内核源码树下的各个kbiuld makefile文件,如果这些东西都有了,那就OK了。安装这些东西可能就是书上说的构建内核源代码树吧。
二、hello world模块
代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
1. #include <linux/init.h>
2. #include <linux/module.h>
3. MODULE_LICENSE(
"Dual BSD/GPL"
);
4.
static
int
hello_init(
void
)
5. {
6. printk(KERN_ALERT
"Hello, Linux.I am coming!\n"
);
7.
return
0;
8. }
9.
static
void
hello_exit(
void
)
10. {
11. printk(KERN_ALERT
"Goodbye, Linux. I will back later.\n"
);
12. }
13. module_init (hello_init);
14. module_exit (hello_exit);
|
说明:
#include <linux/init.h>
#include <linux/module.h>
init.h包含初始化和清除函数。module.h包含有可装载模块需要的大量符号和函数的定义。
尽管不是严格要求,但模块应该指定代码所使用的许可证。内核能够识别的许可证有:“GPL”、“GPL v2”、“GPL and additional rights"、“Dual BSD/GPL"、“GPL MPL/GPL”、“Proprietary”。在我们的模块里我们用“Dual BSD/GPL”
MODULE_LICENSE("Dual BSD/GPL"); // MODULE_LICENSE是一个宏,用来告诉内核模块采用许可证类型。
模块定义了两个函数:static int hello_init(void) static void hello_exit(void)。模块被装载时调用hello_init,模块被移除时调用hello_exit。
这里用到了一个内核函数:prinfk。内核运行时不信赖于C库,内核模块只能调用内核导出的函数。
三、编译和装载
编译模块
makefile代码:
1
2
3
4
5
6
7
8
9
10
11
|
0. ifneq ($(KERNELRELEASE),)
1. obj-m := helloworld.o
2.
else
3. KERNELDIR ?=/lib/modules/$(shell uname -r)/build
4. PWD := $(shell pwd)
5.
default
:
6. $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
7. endif
8. .PHONY: clean
9. clean:
10. -rm *.mod.c *.o *.order *.symvers *.ko
|
说明:
不同于一般的应用程序,我们编译模块的时候都用make来管理我们的编译过程。这个简单的makefile在我们执行make的时候都做了些什么呢?
当我们在模块源代码目录下执行make的时候,它读取当前目录下的makefile,执行else后面的语句,因为这个makefile里没有定义KERNELRELEASE这个变量。展开后是:make -C /lib/modules/2.6.32.12-115.fc12.i686/build M=/home/birdb/LDD3/misc-modules/hello modules,紧接着执行展开后的表达式。-C改变路径到内核源代码,找到内核顶层Makefile,M变量就是模块源代码目录,主要是为了让内核顶层的Makefile包含模块源代码目录下的makefile,包含以后获取obj-m:=hello.o因为内核顶层Makefile定义了KERNELRELEASE。书上说模块源代码目录下的makefile被读取两次,第一次就是执行make的时候,第二次是被包含进内核顶层Makefile的时候。
makefile也可以简单地写成这样:
1
|
obj-m:=hello.o
|
但是不能再简单地执行:make,而应该写成:make -C 内核源代码目录 M=`pwd` modules
在我的系统上运行的结果是:
1
2
3
4
5
6
7
8
9
|
1. [birdb@wishmiss hello]$ make
2. make -C /lib/modules/2.6.32.12-115.fc12.i686/build M=/home/birdb/LDD3/misc-modules/hello modules
3. make[1]: Entering directory `/usr/src/kernels/2.6.32.12-115.fc12.i686'
4. CC [M] /home/birdb/LDD3/misc-modules/hello/helloworld.o
5. Building modules, stage 2.
6. MODPOST 1 modules
7. CC /home/birdb/LDD3/misc-modules/hello/helloworld.mod.o
8. LD [M] /home/birdb/LDD3/misc-modules/hello/helloworld.ko
9. make[1]: Leaving directory `/usr/src/kernels/2.6.32.12-115.fc12.i686'
|
编译成功,接下来就是加载模块。
加载和卸载模块
内核模块加载、卸载的命令分别是:insmod rmmod。加载还可以用modprobe,不过我不会用,也没研究。两个命令都需要用root执行。
执行效果:
1
2
3
4
5
|
1. [birdb@wishmiss hello]$ sudo insmod helloworld.ko;sudo rmmod helloworld.ko
2. [birdb@wishmiss hello]$ dmesg |tail -2
3. Hello, Linux.I am coming!
4. Goodbye, Linux. I will back later.
5. [birdb@wishmiss hello]$
|
初始化和关闭、错误处理也比较重要,书本说得非常详细,请参考书本。
模块参数
测试程序:(加了moduleparam.h头文件)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
# #include <linux/init.h>
# #include <linux/module.h>
# #include <linux/moduleparam.h>
# MODULE_LICENSE("Dual BSD/GPL");
# static char *whom = "wishmiss";
# static int howmany = 3;
# module_param(howmany, int, S_IRUGO);
# module_param(whom, charp, S_IRUGO);
# static int hello_init(void)
# {
# int i;
# for (i = 0; i < howmany; i++)
# printk(KERN_ALERT "(%d) Hello, %s !\n", i, whom);
# return 0;
# }
# static void hello_exit(void)
# {
# printk(KERN_ALERT "Goodbye, Linux.I will back later !%s", whom);
# }
# module_init(hello_init);
# module_exit(hello_exit);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
1. [birdb@wishmiss test]$ make
2. make -C /lib/modules/2.6.32.12-115.fc12.i686/build M=/home/birdb/LDD3/misc-modules/hello/test modules
3. make[1]: Entering directory `/usr/src/kernels/2.6.32.12-115.fc12.i686'
4. CC [M] /home/birdb/LDD3/misc-modules/hello/test/test.o
5. Building modules, stage 2.
6. MODPOST 1 modules
7. CC /home/birdb/LDD3/misc-modules/hello/test/test.mod.o
8. LD [M] /home/birdb/LDD3/misc-modules/hello/test/test.ko
9. make[1]: Leaving directory `/usr/src/kernels/2.6.32.12-115.fc12.i686'
10. [birdb@wishmiss test]$ sudo insmod test.ko
11. [birdb@wishmiss test]$ dmesg |tail -5
12. Hello, Linux.I am coming!
13. Goodbye, Linux. I will back later.
14. (0) Hello, wishmiss !
15. (1) Hello, wishmiss !
16. (2) Hello, wishmiss !
17. [birdb@wishmiss test]$ sudo rmmod test.ko
18. [birdb@wishmiss test]$ dmesg |tail -5
19. Goodbye, Linux. I will back later.
20. (0) Hello, wishmiss !
21. (1) Hello, wishmiss !
22. (2) Hello, wishmiss !
23. Goodbye, Linux.I will back later !wishmiss
24. [birdb@wishmiss test]
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
1. [birdb@wishmiss test]$ sudo insmod test.ko whom=
"xxx"
howmany=2
2. [birdb@wishmiss test]$ dmesg |tail -5
3. (0) Hello, xxx !
4. (1) Hello, xxx !
5. Goodbye, Linux.I will back later !xxx
6. (0) Hello, xxx !
7. (1) Hello, xxx !
8. [birdb@wishmiss test]$ sudo rmmod test.ko
9. [birdb@wishmiss test]$ dmesg |tail -5
10. (1) Hello, xxx !
11. Goodbye, Linux.I will back later !xxx
12. (0) Hello, xxx !
13. (1) Hello, xxx !
14. Goodbye, Linux.I will back later !xxx
15. [birdb@wishmiss test]$
|
具体的参数介绍请参考书本。
至此,第一个内核模块实验完成。