zoukankan      html  css  js  c++  java
  • 3.2Linux的模块驱动

    Linux的模块驱动

    1. 接下来写个最简单的驱动程序,就像程序语言的hello world程序。
    2. 首先是:hello.c的代码:

      这是个最简单的驱动程序。就是打印hello的信息。驱动程序和我们的程序语言结果有点不大一样。驱动模块的入口是倒数第二行的module_init()的函数。驱动模块的出口是module_exit()的函数。

    3.接着是makfile文件:

    这也是一个很简单的Makefile文件了。Obj-m后面跟的是我们的最终目标依赖的文件hello.o。第三行的KDIR是我们编译进的内核的路径。All是执行make得到的目标,$(KDIR)指定内核的路径,就是第三行的路径。M=$(PWD)是模块存放的路径。接着就是清除生成的文件的命令。

    4.make的执行过程:

    从上面的执行的过程,我们可以看到makefile的执行的过程。

    如果在一个工程里,当有两个.c文件的时候的编写:

    Hello.c:

    Function.c:

    Makefile修改为:

    最后编译的结果如下图:

    内核模块的安装和卸载:

    insmod hello.ko

    卸载内核模块:

    rmmod hello(卸载的时候不用加.ko)

    查看模块:

    lsmod

    执行的结果:

    注意:内核模块只有当没有用户用时才可以卸载,如上图:我们的test是没有被使用,而fuse有两个用户在使用。我们试着卸载这两个内核模块的截图:

    内核模块的可选的信息:模块申明、模块参数、符号信息。

    模块的申明:

        MODULE_LICENSE("遵守的协议")

    申明该模块遵守的许可证协议,如:"GPL"、"GPL v2"等。

        MODULE_AUTHOR("作者")

        申明模块的作者

        MODULE_DESCRIPTION("该模块的功能描述")

        MODULE_VERSION("v1.0")

    申明模块的版本

    模块申明可以让读者知道该模块所遵守的协议,增加模块代码的可读性。

    只是一个提示,增加可读性的作用。

    模块参数的传递:

    在我们的应用程序中:int main(int argc,char** argv):argc表示命令行输入的参数个数,argv中保存输入端的参数。

    那么我们的内核模块中是怎么传入参数的呢?:

    模块参数跟我们程序语言的参数有点不大一样,除了用一般的数据类型来申明变量参数,我们还得用module_param()这个宏来指定它是模块参数:

    Module_param(name,type,perm):

    Name:变量的名称

    Type:变量的类型,bool,int,charp。

    Perm:访问权限。S_IRUGO:读权限。S_IWUSR:写权限。

    例如:

    Int a=33;

    Char *st;

    Module_param(a,int ,S_IRUGO);

    Module_param(st,charp,S_IRUGO);

    下面是执行的实例:

    我们定义了一个a=99;然后在14行打印出来。运行的结果:

    上面是执行的过程,我们也可以在执行的时候给它加参数:

    字符串也是一样:

    运行的结果:

    最后是符号导出:

    符号导出的实例:

    修改Makefile:

    修改function.c为:

    执行的过程:

    同时产生了两个.ko模块。

    当我们去安装hello.ko的时候,出现了这个错误:未定义的符号:

    这是因为我们程序中的extern int function();现在的系统中不存在这个函数。那是不是得先insmod function.ko呢!?

    虽然系统已经出现了function函数,可是系统还是找不到。可以看出错误依然存在。

    这就是模块导出的问题:当我们要去使用一个模块里面的变量,函数的时候,必须使用符号导出。也就是把变量和函数输出到我们的系统当中,使整个系统都可以使用。

    修改function.c为:

    用EXPORT_SYMBOL()来申明,我的function是可以被系统的其他模块使用的。不过,我们应该先编译function.ko,在编译hello.ko。结果:

    这就是符号输出的使用。

    总结与应用程序的区别:

    内核的打印:

    Printf和printk都是打印信息的。但是printk还有级别打印:

    Hello.c:

    输出结果:

    结果只有KERN_EMERG级别的才在屏幕打印出来。这样的打印级别,我们可以控制在那些地方可以打印什么。当然,我们也可以用输入来代替级别。例如上面的"<0>",就是KERN_EMER。

  • 相关阅读:
    uCOS的软件定时器、uCOS时钟节拍和滴答定时器的关系
    学习ucosii要用到的几本书
    freertos知识点笔记——队列、二值信号量、计数信号量
    《嵌入式软件设计基础——基于ARM Cortex—M3》读书笔记
    大小端测试C实现
    static 的三个作用
    基于IAR6或者IAR7建立STM32开发工程(通过实际测试,使用IAR6.30.4)
    STM32中stm32f0xx_flash.icf文件的作用详解!(不错的!)
    CRC点滴
    int *a[] 与(int *)a【5】的区别
  • 原文地址:https://www.cnblogs.com/FORFISH/p/4201880.html
Copyright © 2011-2022 走看看