编译:
|
|
加载该驱动:
|
|
打印一行信息,该字符串由程序中的
hello_init 函数实现的。
查看驱动:
|
|
卸载该驱动:
|
|
打印一行信息,该字符串由程序中的
hello_exit 函数实现的。
如果在 XWindow 下面,会看不到内核输出,可以切换到文本界面下查看。
以下是网上资料:
http://blog.csdn.net/xdxiaodao/archive/2008/05/22/2470963.aspx
学习心得:
(1)驱动模块运行在内核空间,运行时不能依赖于任何函数库和模块连接,所以在写驱动时所调用的函数只能是作为内核一部分的函数。
(2)驱动模块和应用程序的一个重要不同是:应用程序退出时可不管资源释放或者其他的清除工作,但模块的退出函数必须仔细撤销初始化函数所作的一切,否则,在系统重新引导之前某些东西就会残留在系统中。
(3)处理器的多种工作模式(级别)其实就是为了操作系统的用户空间和内核空间设计的。在Unix类的操作系统中只用到了两个级别:最高和最低级别。
(4)要十分注意驱动程序的并发处理。
(5)内核API中具有双下划线(_ _)的函数,通常是接口的底层组件,应慎用。
(6)内核代码不能实现浮点书运算。
(7)Makefile文件分析:
obj-m := hello.o 代表了我们要构造的模块名为hell.ko,make 会在该目录下自动找到hell.c文件进行编译。如果 hello.o是由其他的源文件生成(比如file1.c和file2.c)的,则在下面加上(注意红色字体的对应关系):
hello-objs := file1.o file2.o ......
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
其中 -C $(KERNELDIR) 指定了内核源代码的位置,其中保存有内核的顶层makefile文件。
M=$(PWD) 指定了模块源代码的位置
modules目标指向obj-m变量中设定的模块。
(8)insmod使用公共内核符号表来解析模块中未定义的符号。公共内核符号表中包含了所有的全局内核项(即函数和变量的地址),这是实现模块化驱动程序所必须的。
(9)Linux使用模块层叠技术,我们可以将模块划分为多个层,通过简化每个层可缩短开发周期。如果一个模块需要向其他模块到处符号,则使用下面的宏:
EXPORT_SYMBOL(name);
EXPORT_SYMBOL_GPL(name);
符号必须在模块文件的全局变量部分导出,因为这两个宏将被扩展为一个特殊变量的声明,而该变量必须是全局的。
(10)所有模块代码中都包含一下两个头文件:
#include <linux/init.h>
#include <linux/module.h>
(11)所有模块代码都应该指定所使用的许可证:
MODULE_LICENSE("Dual BSD/GPL");
此外还有可选的其他描述性定义:
MODULE_AUTHOR("");
MODULE_DESCRIPTION("");
MODULE_VERSION("");
MODULE_ALIAS("");
MODULE_DEVICE_TABLE("");
上述MODULE_声明习惯上放在文件最后。
(12)初始化和关闭
初始化的实际定义通常如下:
static int _ _init initialization_function(void)
{
/*初始化代码*/
}
module_init(initialization_function)
清除函数的实际定义通常如下:
static int _ _exit cleanup_function(void)
{
/*清除代码*/
}
module_exit(cleanup_function)
