#ifndef MODULE
#define MODULE
#endif
#include //所有模块都需要的头文件
#include
#include
#include // init和exit相关宏
MODULE_LICENSE("GPL");
int text_init(void){
printk("<0>Hello World!");
return 0;
}
void text_cleanup(void){
printk("<0>Goodbye World!");
}
module_init(text_init); //注册加载时执行的函数
module_exit(text_cleanup); //注册卸载时执行的函数
一个Linux内核模块需包含模块初始化和模块卸载函数,前者在insmod的时候运行,后者在rmmod的时候运行。初始化与卸载函数必须在宏module_init和module_exit使用前定义,否则会出现编译错误。
程序中的MODULE_LICENSE("GPL")用于声明模块的许可证。
编译:
gcc -c -I /usr/src/linux-2.4/include/ hello.c
运行:
insmod hello.o
终端上会显示:
localhost kernel:Hello World!
同时在/proc/modules里面会看到相应的设备信息:
more /proc/modules
你会看到(或许后面的数值不一样):
hello 844 0 (unused)
.......
卸载驱动程序:
rmmod hello
你将会在终端上面看到:
localhost kernel:Goodbye World!
模块编程需要注意:
1.在gcc编译选项中增加-c
2.在gcc编译选项中定义两个宏:-DMODULE -D__KERENL__
或直接在源文件中定义这两个宏:
#define MODULE
#define __KERNEL__
3.在源文件中包括module.h文件:
#i nclude
4.假定你现在运行的内核的源码目录绝对路径是MyKernelSrcPath,在gcc编译时增加选项:
-I $MyKernelSrcPath/include (如-I /usr/src/linux/include)
5.某些时候用insmod -f能够成功加载,但需谨慎使用。
6.如果看不到用printk打印的信息,可以用dmesg命令看。
7.打印消息受级别的限制,消息级别可以通过printk设置,如:
printk("something"); /* 其中0<=n<=7 */
假设控制台的消息级别为m, 当n 这样一方面可以提高要打印消息本身的级别(数字越小级别越高),
另一方面可以改变控制台的消息级别(可从1到8),如改为8可用以下命令:
# echo "8" > /proc/sys/kernel/printk
关于内核模块初始化(加载)函数
当用户输入命令“insmod 模块文件名”(或者其他方式)加载内核模块时,系统会检测此模块能否被加载,如果能被加载,内核调用模块的初始化函数。在linux 2.4中,内核模块的初始化函数名为init_module()。但如果驱动程序需要编译进内核,则初始化函数不能与内核的其他部分(包括其他内核模块)的函数同名。这样,如果程序可能编译到内核中时就比较麻烦。不过在这个头文件中定义了宏module_init(),用来屏蔽两者的差别,事实上,使用这个宏可以使驱动程序向上兼容linux 2.6,而兼容linux 2.0也比较方便。
关于内核模块清除(卸载)函数
当用户输入命令“rmmod 模块文件名”(或者其他方式)卸载内核模块时,此时,系统会检测此模块是否能被卸载,内核将调用模块清除函数。在linux 2.4中,清除函数的函数名称为cleanup_module()。但如果驱动程序需要编译进内核,则初始化函数不能与内核的其他部分(包括其他内核模块)的函数同名。这样,如果程序可能编译到内核中时就比较麻烦。不过在这个头文件中定义了宏module_exit(),用来屏蔽两者的差别。