linux驱动之hello_world源码与编译

开始了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)所有的代码中都包含代码中的两个头文件

 

2MODULE_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定义了这条消息的优先级。

 

6module_init()的使用时是强制的。这个宏会在模块的目标代码中增加一个特殊的段,用于说明内核初始化函数所在的位置。没有这个定义,初始化函数用于不会被调用。

 

7)每个模块都会需要一个清除函数,在模块被卸载时调用,告诉内核:我要离开啦,不要再让我做任何事情了。清除函数要撤销初始换函数所做的一切。清除函数没有返回值,被声明为void__exit标记该代码仅用于模块卸载。如果一个模块未定义清除函数,则内核不允许卸载该模块。

 

8module_exit()声明对于内核找到模块的清除函数是必需的。

二、hello world编译:

 

编译器:gcc

 

编译命令工具:GNU make

 

编译文件:Makefile

 

Hello worldmakefile文件:

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.模块卸载:

 

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。