hello world驱动设计(x86)

1. 这个驱动有啥不同

        最近发现很多教程,上来就是往写led驱动,对于新手来说,理解起来是十分费劲的,因为一个led的驱动涉及到许多的知识,加之写led驱动还需要开发板,这就受不了了,哪来那么多钱!所以最近在复习驱动设计时看到了陈莉君老师的视频教程Linux内核分析与应用[1],看到了一种更适合作为驱动设计Hello world的写法,因此有了这篇博文。事不宜迟主要介绍一下这个驱动设计有啥不同:

  • 不需要硬件操作
  • 不需要开发板
  • 不需要编译内核源码
  • 不需要写应用程序
  • 只需要一个ubuntu系统

[1] 其中P5有详细的流程介绍

2. 驱动的实现

2.1 驱动开发的架构
hello world驱动设计(x86)_第1张图片
图2-1 驱动开发架构示意图
  • 驱动程序源程序:驱动的具体实现代码,同时包含驱动的基本信息;
  • 内核源码:由于驱动需要动态的加载到内核中,Linux的内核版本非常多,为了更好的兼容当前的内核版本,驱动的编译需要借助相对应的内核源码,进入内核源码获取相关的makefile和系统参数;
  • 编译脚本Makefile:Makefile的的功能是自动化编译,主要用在大型的工程项目中,属于初学者的我们也应该接触Makefile的相关知识,用起来很爽;
  • 驱动模块:Linux中“一切皆文件”,驱动模块也仅仅是个文件而已;
  • insmod:属于Linux提供的驱动模块安装的命令,常用的驱动模块相关的命令有rmmod、lsmod和modinfo;
  • 操作系统内核:驱动运行的载体,也是整个操作系统的核心。
2.2 驱动源文件
#include 
#include 

// 定义模块入口函数
static int __init hello_world_init(void)
{
	printk("Hello world!\n");
	return 0;
}

// 定义模块出口函数
static void __exit hello_world_exit(void)
{
	printk("Goodbye world!\n");
}

// 注册模块出口、入口函数
module_init(hello_world_init);
module_exit(hello_world_exit);

// 模块其他信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("www.czw1998.icu");
MODULE_DESCRIPTION("This is a hello world module!");

        编译驱动并非使用系统自带的gcc编译器,而是使用内核源码中的编译器,我们指定的头文件目录也有所差别。驱动程序设计所用到的头文件通常位于内核源码中的include/linux/。对于ubuntu中的内核源码位于 /usr/src/。但是你会发现这个目录下可能还有多个文件夹如图2-2,那么怎么确定自己的内核版本呢?可以使用 uname -r查看当前系统的内核版本如图2-2

hello world驱动设计(x86)_第2张图片
图2-2 如何确定内核源码包以及版本

        驱动模块必须通过模块安装命令(insmod)才能安装到内核中,同时已安装的驱动模块也只能通过模块卸载命令(rmmod)才能从内核中卸载,在模块的安装/卸载中,通常涉及到硬件资源的申请/释放,为了完成这一操作,Linux引入模块入口以及出口函数的机制,模块安装系统将调用module_init注册的函数,模块的卸载系统将调用module_exit注册的函数

        模块除了具体的实现代码之外,模块还需要添加一些模块的其他信息(不是必须),方便模块的使用者更好的了解模块,可以通过modinfo查看模块信息,比如如下查看hello_world.ko信息

hello world驱动设计(x86)_第3张图片
图2-3 使用modinfo查看模块信息
2.3 驱动编译脚本Makefile
obj-m=hello_world.o
# 定义一些变量
PWD=$(shell pwd)
KERNEL_VERSION=$(shell uname -r)
KERNEL_PATH=/usr/src/linux-headers-$(KERNEL_VERSION)

default:
	make -C $(KERNEL_PATH) M=$(PWD) modules
clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean

        编译驱动模块最好借助Makefile,可以大大的简化你重复编译的繁琐性,同时可以使得编译的操作流程更加清晰,简单的解读一下如上的Makefile文件:

  • obj-m:指定生成的目标文件;
  • PWD、KERNEL_VERSION、KERNEL_PATH:自定义shell变量(名字理论上任意必须符合变量命名规则,但原则上应该通俗易懂),使用变量将必要的环境参数记录,方便后面重复使用;
  • default、clean:使用 make 命令时可以加上许多参数,不带参数则是 default ,我们可以使用 make clean 清除项目生成文件;
  • -C、M:可以发现这里压根就没有引入编译器命令,原因是我们借助的是内核源码中的Makefile完成我们的编译,-C表示进入内核目录,M生成文件的存储目录,我么从编译过程的输出信息可以很直观的看到这个过程如图2-4
  • modules、clean:上面我们说了,make可以带参数,这两个则是当前Makefile传到内核源码Makefile中的参数。
hello world驱动设计(x86)_第4张图片
图2-4 make输出信息

3. 实验

3.1 驱动模块的编译
hello world驱动设计(x86)_第5张图片
图3-1 编译
3.2 驱动模块的安装及信息查看
hello world驱动设计(x86)_第6张图片
图3-2 驱动模块的安装及信息查看
  • grep介绍:用于查找文件中相对应的字符串,这里使用此命令可以更方面的展示,避免截图过长,这个命令同学们也可以去接触一下,非常好用;
  • dmesg:由于printk输出的信息并不会显示在终端中,我们需要查看系统日志文件,dmesg命令可以方便的输出系统开机以来全部的日志信息。
3.1 驱动模块的卸载及信息查看
图3-3 驱动模块的卸载及信息查看

你可能感兴趣的:(嵌入式ARM,linux,驱动设计)