• Linux驱动实践:带你一步一步编译内核驱动程序


    学习的困惑

    记得以前我在开始学习驱动开发的时候,找来很多文章、资料来学习,但是总是觉得缺少了点全局视角。

    就好像:我想看清一座山的全貌,但总是被困在一个、又一个山谷中一样。

    主要的困惑有 3 点:

    1. 每一篇文章的介绍都是正确的,但是如果把很多文章放在一起看,就会发现怎么说的都不一样啊?

    2. 有些文章注重函数的介绍,但是缺乏一个全局的视角,从整体上来观察驱动程序的结构;

    3. 对于一个新手来说,能够边学习、边实践,这是最好的学习方式,但是很多文章不会注意这方面。虽然文章内容很漂亮,但是不知道怎么去实践、验证。

    因此,这几篇文章我们就从最简单的驱动模块编译开始,然后介绍字符设备驱动程序。

    在这部分,会以 GPIO 为例子,重点描述其中的关键节点。

    最后再介绍在中断处理程序中,如何利用信号量、小任务、工作队列,把内核事件传递到应用层来处理。

    作为第一个开篇文章,从最简单的内核编译开始。

    实际操作一下:如何把一个最简单的驱动程序(hello),按照 2 种方式进行编译:

    1. 编译进内核;

    2. 编译为一个独立的驱动模块;

    实践环境

    为了便于测试,以下操作都是在 Ubuntu16.04 操作系统里完成的。

    编译Linux驱动程序,肯定需要内核源码,这里选择的是 linux-4.15 版本,可以在官网下载。

    文末有下载方式。

    下载之后,把linux-4.15.tar.gz解压到Ubuntu中任意目录即可,例如:解压到~/tmp/目录下:

    $ tar -zxvf linux-4.15.tar.gz -C ~/tmp/
    

    编译进内核

    创建驱动程序目录

    linux 中的驱动,一般都放在 linux-4.15/drivers/ 目录下,因此在这个目录中创建一个hello文件夹。

    $ mkdir linux-4.15/drivers/hello
    

    对于一个驱动来说,最重要的就是3个文件:

    1. 源代码

    2. Kconfig

    3. Makefile

    只要按照固定的格式来编写这3个文件,linux内核的编译脚本就可以确保把我们的驱动程序编译进去。

    创建源文件

    首先是源码,在hello文件夹中创建源文件 hello.c

    1. $ cd linux-4.15/drivers/hello
    2. $ touch hello.c

    源文件hello.c的内容是:

    1. #include
    2. #include
    3. // 当驱动被加载的时候,执行此函数
    4. static int __init hello_init(void)
    5. {
    6. printk(KERN_ALERT "welcome, hello"\n");
    7. return 0;
    8. }
    9. // 当驱动被卸载的时候,执行此函数
    10. static void __exit hello_exit(void)
    11. {
    12. printk(KERN_ALERT "bye, hello\n");
    13. }
    14. // 版权声明
    15. MODULE_LICENSE("GPL");
    16. // 以下两个函数属于 Linux 的驱动框架,只要把驱动两个函数地址注册进去即可。
    17. module_init(hello_init);
    18. module_exit(hello_exit);

    有两个小地方注意一下:

    1. 在内核中,打印函数是 printk,而不是 printf;

    2. 打印信息的级别有好几个,从 DEBUG 到 EMERG,这里使用的是 KERN_ALERT,方便查看打印信息。

    创建 Kconfig 文件

    这个文件是用来对内核进行配置的,当执行 make menuconfig 指令的时候,这个文件就被解析。

    先创建文件:

    1. $ cd linux-4.15/drivers/hello
    2. $ touch Kconfig

    添加如下内容:

    1. config HELLO
    2. tristate "hello driver"
    3. help
    4. just a simplest driver.
    5. default y

    第一行内容 config HELLO ,在执行配置的时候,将会生成一个变量 CONFIG_HELLO ,而这个变量,将会在编译的时候,被 Makefile 引用。

    最后一行的 default y ,就表示把 CONFIG_HELLO 的值设置成 y,从而让这个驱动被编译到内核中。

    现在,hello驱动中的KConfig配置文件已经准备好了,但是还需要这个配置文件登记到 Linux 内核的整体配置文件中。

    也就是把它登记在 linux-4.15/drivers/Kconfig 文件的末尾:

    1. source "drivers/hello/Kconfig"
    2. endmenu // 加在这一句的上面

    现在,可以来执行下面指令,看一下具体的配置界面:

    1. $ cd linux-4.15/
    2. $ make distclean
    3. $ make ARCH=x86_64 defconfig
    4. $ make ARCH=x86_64 menuconfig

    2条指令,是用来把默认的配置保存到当前目录下的 .config 配置文件,也就是把一个默认的配置文件复制过来,作为我们自己的配置文件。

    以后再修改配置参数时,修改的内容就会存储在 .config 文件中,

    3条指令,是用来配置内核的,可以进入 Device Drivers 菜单,然后在最底层看到我们的 hello driver 被标记成星号, 这表示被编译进内核。

    按向下方向键,把高亮定位到 Device Drivers ---> ,然后敲回车键,进入到 Device Drivers 的配置界面。

    按向下方向键,一直到最后一个条目,就可以看到我们的 hello 驱动了,如下:

    可以看到 hello driver 前面显示的是型号 *,这表示:该驱动将会编译进内核。

    我们可以按下空格键试一下,会在三种标记中切换:型号,M,空值。M 标记意思是编译成驱动模块。

    我们这里选择星号(编译进内核),然后按下右方向键,最下方的几个按键的焦点移动到按钮上:

    按下回车键,就会弹出保存对话框,选择默认保存文件 .config 即可,然后在按钮高亮的时候,按下回车键即可保存。

    此时,在弹出的确认窗口中,选择 ,按下回车键即可:

    此时,返回到 Device Drivers 的配置界面,在最下面的按钮中,选择让  高亮,然后一路退出即可。

    创建 Makefile 文件

    Makefile 文件是make工具的脚本,首先创建它:

    1. $ cd linux-4.15/drivers/hello
    2. $ touch Makefile

    其中的内容只有一行:

    obj-$(CONFIG_HELLO) += hello.o
    
    1. CONFIG_HELLO 可以看做一个变量,在编译的时候,这个变量的值可能是:y, n 或者 m。

    2. 在刚才的 Kconfig 参数配置中,CONFIG_HELLO 被设置为 y,于是这句话就被翻译成:obj-y += hello,表示把 hello 驱动编译进内核。

    现在,hello驱动程序的Makefile已经创建好了,我们还要让linux内核的编译框架知道这个文件才行。

    在文件 linux-4.15/drivers/Makefile 中的末尾,添加如下内容:

    obj-$(CONFIG_HELLO)    += hello/
    

    编译

    万事俱备,只欠编译!依次执行如下指令:

    1. $ cd linux-4.15/
    2. $ make -j4

    make指令执行结束之后,编译得到的内核中(vmlinux)就包含了我们的hello驱动。

    编译为驱动模块

    编译为驱动模块,也有两种 操作方式:

    编译所有的驱动模块

    1. 在执行 make ARCH=x86_64 menuconfig 指令的时候,把 hello 配置成 M;

    2. 然后在 linux-4.15 中执行编译模块指令:make -j4 modules。

    编译成功之后,就可以得到文件: linux-4.15/drivers/hello/hello.ko。

    这样的编译指令,是把所有的模块都编译了一次(在输出信息中,可以看到编译了很多模块)。

    只编译 hello 这一个驱动模块

    另外一种编译驱动模块的方式是:进入hello目录,只编译这一个驱动模块。

    这种编译方法,就需要修改hello目录下的Makefile文件了,内容如下:

    可以把 hello 目录下的所有文件删除,只保留源文件 hello.c,然后新建 Makefile 文件。

    1. ifneq ($(KERNELRELEASE),)
    2. obj-m := hello.o
    3. else
    4. KERNELDIR ?= /lib/modules/$(shell uname -r)/build
    5. PWD := $(shell pwd)
    6. default:
    7. $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
    8. clean:
    9. $(MAKE) -C $(KERNEL_PATH) M=$(PWD) clean
    10. endif

    然后,在hello文件夹中执行make指令,即可得到驱动模块  hello.ko 。

    验证一下

    加载驱动:

    1. $ cd linux-4.15/drivers/hello
    2. $ sudo insmod ./hello.ko

    此时终端窗口是没有任何输出的,需要输入指令 dmesg | tail ,可以看到  hello_init 函数的输出内容:

    卸载驱动:

    $ sudo rmmod hello
    

    再次输入 dmesg | tail ,可以看到 hello_exit 函数的输出内容:

    原文链接:https://mp.weixin.qq.com/s/L8Y3Uw7Lida8N5YvK40BWg 


    学习更多相关资料及视频
    DPDK 学习资料、教学视频和学习路线图 :https://space.bilibili.com/1600631218
    Dpdk/网络协议栈/ vpp /OvS/DDos/NFV/虚拟化/高性能专家 上课地址: https://ke.qq.com/course/5066203?flowToken=1043799
    DPDK开发学习资料、教学视频和学习路线图分享有需要的可以自行添加学习交流q 君羊909332607备注(XMG) 获取
     

  • 相关阅读:
    浏览器自动化利器Selenium IDE使用指南
    Java Day1
    计算机毕业设计django基于python图书馆借阅系统(源码+系统+mysql数据库+Lw文档)
    基于VSR-GUI的视频去字幕水印
    【代码随想录】链表刷题
    Eclipse搭建struts2框架
    Springboot毕设项目高校食堂饭卡管理824ct(java+VUE+Mybatis+Maven+Mysql)
    游戏使用脚本配置HTTP
    opengl动态修改vbo数据
    代码随想录-026-15.三数之和
  • 原文地址:https://blog.csdn.net/weixin_60043341/article/details/126386952