• 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! "); /*这里是初始化代码*/

    return 0;

    }

    static void __exit hello_exit(void)  //清除函数

    {

    printk(KERN_ALERT "Goodbye,cruel world! "); /*清除代码*/

    }

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

     

     

  • 相关阅读:
    PV、UV、GMV
    保存Hive查询结果的方法 insert overwrite 用法
    Hive substr 函数截取字符串
    HIVE中join、semi join、outer join
    Hive 差集运算
    gitlab和github区别
    前端工程化 ESlint 配置
    ES6 WeakMap Map 区别
    js 创建数组方法以及区别
    eslint for...in 报错处理
  • 原文地址:https://www.cnblogs.com/from-201410/p/4010805.html
Copyright © 2020-2023  润新知