当前位置: 首页 > 科技观察

Linux驱动实战:带你一步步编译内核驱动

时间:2023-03-13 19:05:57 科技观察

别人的经验,我们的阶梯!大家好,我是刀哥。今天给大家分享一些笔记本中的一些盘点:Linux系统中的驱动和中断。大概有6~7篇文章会由浅入深地介绍Linux中的驱动程序编程方法。文章顺序也是我自学的顺序。之前的学习记录比较零散,现在只是按照一定的顺序重新整理一下。在这些文章中,理论知识会比较少,而更注重实际操作。操作指南的所有代码我都会上传到网盘,文末有下载说明。只要按照文中介绍的步骤进行操作,就可以成功操作。学习上的迷茫记得刚开始学习驱动开发的时候,找了很多文章和资料研究,但总觉得自己缺乏全局观。就好比:想看清一座山的全貌,却总是困在一个又一个的山谷里。混淆点主要有3点:每篇文章的介绍都是正确的,但是多篇文章一起看,会发现用词不一样?有的文章侧重于功能的介绍,而缺乏从全局驱动的角度,从整体上观察驱动的结构;对于新手来说,边学边练是最好的方法,但是很多文章不会关注这方面。虽然文章的内容很漂亮,但我不知道如何实践和验证。因此,在这些文章中,我们都是从编写最简单的驱动模块开始,然后介绍字符设备驱动。本部分将以GPIO为例,着重描述关键节点。最后,我们将介绍如何在中断处理程序中使用信号量、小任务和工作队列将内核事件传递给应用层进行处理。作为第一篇开篇,先从最简单的内核编译说起。实操:如何编译最简单的驱动程序(hello),两种方式:编译进内核;编译成一个独立的驱动模块;在实践环境中,为了测试方便,以下操作均在系统中完成的Ubuntu16.04上进行。要编译Linux驱动程序,内核源代码是必不可少的。这里选择linux-4.15版本,可以到官网下载。文末有下载方法。下载后,将linux-4.15.tar.gz解压到Ubuntu任意目录,例如:解压到~/tmp/目录:$tar-zxvflinux-4.15.tar.gz-C~/tmp/compileintokernel创建一个driver目录linux中的驱动一般都放在linux-4.15/drivers/目录下,所以在该目录下创建一个hello文件夹。$mkdirlinux-4.15/drivers/hello对于一个驱动来说,最重要的是3个文件:源代码KconfigMakefile只要这3个文件写成固定的格式,linux内核的编译脚本就可以保证我们的驱动程序被编入其中。创建源文件首先是源代码,在hello文件夹下创建一个源文件hello.c:$cdlinux-4.15/drivers/hello$touchhello.c源文件hello.c的内容为:#include#include//当驱动被加载时,执行这个函数staticint__inithello_init(void){printk(KERN_ALERT"welcome,hello"\n");return0;}//当驱动被unloaded,Executethisfunctionstaticvoid__exithello_exit(void){printk(KERN_ALERT"bye,hello\n");}//版权声明MODULE_LICENSE("GPL");//以下两个函数属于Linux的驱动框架,只要作为驱动的两个函数注册地址就可以了。module_init(hello_init);module_exit(hello_exit);有两个小地方需要注意:在内核中,打印函数是printk,不是printf;打印信息有几个层次,从DEBUG到EMERG,这里用到KERN_ALERT,方便查看打印信息,创建一个Kconfig文件,这个文件是用来配置内核。当执行makemenuconfig命令时,这个文件将被解析。先创建一个文件:$cdlinux-4.15/drivers/hello$touchKconfig添加如下内容:configHELLOtristate"hellodriver"helpjustasimplestdriver.defaulty第一行内容configHELLO,在执行配置的时候,会生成一个变量CONFIG_HELLO,这个变量为Makefile在编译时会引用它。最后一行默认的y表示CONFIG_HELLO的值设置为y,这样驱动就被编译进了内核。现在,hello驱动中的KConfig配置文件已经准备好了,但是这个配置文件需要注册到Linux内核的整体配置文件中。也就是在linux-4.15/drivers/Kconfig文件末尾进行注册:source"drivers/hello/Kconfig"endmenu//在上面加上这句话现在,可以执行如下命令查看具体的配置界面:$cdlinux-4.15/$makedistclean$makeARCH=x86_64defconfig$makeARCH=x86_64menuconfig第二条命令用于将默认配置保存到当前目录下的.config配置文件中,即复制一个默认配置文件作为我们自己的配置文件。以后修改配置参数时,修改的内容会保存在.config文件中。第三条命令用于配置内核。可以进入DeviceDrivers菜单,然后看到我们的hello驱动被标记为一个星号,表示已经编译进内核了。按向下键将高亮定位到DeviceDrivers--->,然后按回车键进入DeviceDrivers配置界面。一直按向下键,直到最后一项,就可以看到我们的hello驱动,如下:可以看到hello驱动前面显示了型号*,意思是:驱动会被编译进内核。我们可以按空格键试一试,它会在model、M、null三种标签之间切换。M标志表示编译成驱动模块。这里我们选择星号(编译进内核),然后按右方向键,最下面几个键的焦点移到按钮上:按回车键,会弹出一个保存对话框,选择默认保存文件.config就可以了,然后当按钮高亮的时候,按回车键保存。此时,在弹出的确认窗口中,选择,按回车键:此时,返回DeviceDrivers配置界面,在最下方的按钮中,选择高亮,然后一路退出。创建MakefileMakefile是make工具的脚本,先创建:$cdlinux-4.15/drivers/hello$touchMakefile,内容只有一行:obj-$(CONFIG_HELLO)+=hello.oCONFIG_HELLO即可看成是一个变量,在编译的时候,这个变量的值可能是:y,n或者m。刚才的Kconfig参数配置中,CONFIG_HELLO设置为y,所以这句话翻译成:obj-y+=hello,意思是将hello驱动编译进内核。现在,hello驱动的Makefile已经创建好了,我们需要让linux内核的编译框架知道这个文件。在文件linux-4.15/drivers/Makefile的末尾添加如下内容:obj-$(CONFIG_HELLO)+=hello/编译一切就绪,只需要编译!依次执行以下指令:$cdlinux-4.15/$make-j4make指令执行后,编译好的内核(vmlinux)就包含了我们的hello驱动。CompileasadrivermoduleCompileasadrivermodule,也有两种操作方式:编译所有驱动模块,执行makeARCH=x86_64menuconfig命令时将hello配置为M;然后在linux-4.15中执行编译模块命令:make-j4modules。编译成功后,可以得到文件:linux-4.15/drivers/hello/hello.ko。这样的编译指令是对所有模块进行一次编译(在输出信息中可以看到编译了很多模块)。只编译驱动模块hello另一种编译驱动模块的方法是进入hello目录,只编译驱动模块。这种编译方式需要修改hello目录下的Makefile。内容如下:可以删除hello目录下的所有文件,只保留源文件hello.c,然后新建一个Makefile。ifneq($(KERNELRELEASE),)obj-m:=hello.oelseKERNELDIR?=/lib/modules/$(shelluname-r)/buildPWD:=$(shellpwd)默认:$(MAKE)-C$(KERNELDIR)M=$(PWD)modulesclean:$(MAKE)-C$(KERNEL_PATH)M=$(PWD)cleanendif然后在hello文件夹下执行make命令,得到驱动模块hello.ko。验证加载的驱动:$cdlinux-4.15/drivers/hello$sudoinsmod./hello.ko此时终端窗口没有任何输出,需要输入命令dmesg|tail,可以看到hello_init函数的输出:unloadthedriver:$sudormmodhelloEnterdmesg|再次tail,可以看到hello_exit函数的输出:本文转载自微信公众号《IOT物联网小镇》