开始了linux驱动的学习,从最简单的hello world开始。
一、hello world源码及注释如下所示:
#include <linux/init.h> /*必须的头文件,用于初始化和清除函数的头文件*/
#include <linux/module.h> /*必须的头文件,含有装载模块需要的大量符合和函数的定义, 必须包含在模块源代码中*/
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TX_fpga");
MODULE_DESCRIPTION("Hello world test");
MODULE_VERSION("V1.0");
static int __init hello_init(void) //初始化函数
{
printk(KERN_ALERT "Hello.......................world!\n"); /*这里是初始化代码*/
return 0;
}
static void __exit hello_exit(void) //清除函数
{
printk(KERN_ALERT "Goodbye,cruel world!\n"); /*清除代码*/
}
module_init(hello_init); #用于指定模块初始化的宏
module_exit(hello_exit);#用于指定模块清除函数的宏
注解:
(1)所有的代码中都包含代码中的两个头文件
(2)MODULE_LICENSE("Dual BSD/GPL"); 是一个特殊的宏,用来告诉内核该模块采用的许可证,所有模块应该都包含的。如果没有这样的声明,内核在装载该模块时就会被被“污染”。"Dual BSD/GPL (BSD/GPL双重许可)"、"Dual MPL/GPL (MPL/GPL双重许可)"、"GPL v2 (GPL版本2)"、"GPL and additional rights (GPL及附加权利)"以及"Proprietary (专有)",像PCIE驱动中使用的MODULE_LICENSE("Avnet Design Services");
(3)还可以在模块中包含其他描述性定义,如上所述等,如PCIE驱动中的MODULE_DEVICE_TABLE(pci, ads_pcie_dma_ids);用来告诉用户空间模块所支持的设备。
(4)初始化函数在模块被装载到内核时调用,被声明为static,因为这种函数在特定文件之外没有任何其他意义。__init对内核来讲是一种暗示,表明该函数仅在初始化期间使用。在模块被装载之后,模块装载器就会将初始化函数扔掉,这样可将该函数占用的内存释放出来。模块初始化函数的任务是为以后调用模块函数预先做准备,就像模块在说:我在这儿,并且我能做这些工作。
(5)函数printk()在Linux内核中定义,功能和标准C库中的函数printf类似,内核需要自己单独的打印函数,这是因为它在运行时不能依赖于C库。模块能够调用printk()是因为在insmod函数装入模块后,模块就连接到了内核,因而可以访问内核的公用符号(包括函数和变量)。代码中的字符串KERN_ALERT定义了这条消息的优先级。
(6)module_init()的使用时是强制的。这个宏会在模块的目标代码中增加一个特殊的段,用于说明内核初始化函数所在的位置。没有这个定义,初始化函数用于不会被调用。
(7)每个模块都会需要一个清除函数,在模块被卸载时调用,告诉内核:我要离开啦,不要再让我做任何事情了。清除函数要撤销初始换函数所做的一切。清除函数没有返回值,被声明为void。__exit标记该代码仅用于模块卸载。如果一个模块未定义清除函数,则内核不允许卸载该模块。
(8)module_exit()声明对于内核找到模块的清除函数是必需的。
二、hello world编译:
编译器:gcc
编译命令工具:GNU make
编译文件:Makefile
Hello world的makefile文件:
obj-m := hello.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
注解:
(1)第一行中:=表示对变量赋值,此句要表达的是有一个内核模块需要从目标文件hello.o中构造,构造的模块名为hello.ko。
(2)第二行对KDIR变量赋值,该变量指代内核源码目录。
(3)第三行是对PWD变量的赋值,作用是将$(shell pwd)的返回结果即当前目录赋值给PWD,用来指代我们要编译的驱动程序所在的位置。”$”表示调用后面的变量。
(4)第四行和第五行为makefile的规则,all为规则目标也是终极目标,第五行为规则的命令,命令行必须以[TAB]字符开始,[TAB]字符告诉make此行是一个命令行。-C选项的作用是将当前工作目录转移到所指定的位置,即内核的构造树,先执行该路径下的makefile,定位内核的源代码目录。”M=”选项的作用是:当用户需要以某个内核为基础编译一个外部模块的时候,需要在make modules命令中加入”M=dir”,程序会自动跳到所指定的dir(即工程所在的目录)中查找模块源码,将其编译,modules目标文件指向obj-m变量中设定的模块,即hello.o(中间文件),最后生成hello.ko文件。
(5)第六行和第七行是一个清除命令,执行该命令时之前生成的文件都被清除掉。
如上所述,在shell命令解析器中输入make命令生成hello.ko文件。
三、hello world装载与卸载
1.模块装载与查询:
注解:
(1)insmod后面的./不可以省略,表示在当前目录下查找hello.ko文件,如果不加上,insmod不会在当前目录找,最终的结果就是找不到。
(2)lsmod命令用来查询已经添加的内核模块。
(3)装载之后的打印信息在系统日志文件里,/var/log/kern.log中,可以用$ tail /var/log/kern.log命令或$ dmesg命令查看。
2.模块卸载: