有多少Linux驱动工程师是通过《Linux设备驱动程序》这本书入门的呢?今天翻到了抽屉里的这本书,想想也是挺感慨的。
所以重新翻开这本书,通过文章来记录并实践其中的知识要点,加强自己对于Linux驱动的理解。
1. 一些概念
- 机制:需要提供什么功能
- 策略:如何使用这些功能
在编写驱动程序时,应该针对机制编程,而不要强加策略,策略的添加留给上层的应用去添加,这样做的目的是使得驱动程序会更加灵活。
设备分类:
- 字符设备:能够像字节流(文件)一样被访问的设备,主要实现
open
、close
、read
、write
等系统调用。文件节点位于/dev
目录下。 - 块设备:一次性只能访问一个或者多个完整块,每个块包含512甚至更多的字节。文件节点也位于
/dev
目录下。 - 网络接口:一个能够和其他主机交换数据的设备,不存在对应的节点,有一套专门的传输函数。
2. 环境搭建
要开发驱动自然需要有Linux系统。我这边选用的是最新的Ubuntu 2020,也可以使用CentOS、DeepIn等操作系统,根据自己的习惯选择就好。
去官网下载ISO镜像文件,安装一个虚拟机或者直接用U盘安装到电脑上都可以,这些应该不需要过多介绍了。还有一些操作过程中的权限问题,也不再具体说明了。
在写驱动代码前我们还需要准备好内核树,什么是内核树呢?
可以理解为内核源码。针对当前的操作系统编写驱动程序,需要与源码中的目标链接,这样编译出来的驱动模块才能够在当前的操作系统中正常运行。
如何准备内核树呢?
其实安装完Ubuntu系统后,其提供了 linux-header
,路径为 /usr/src/
,如下图所示。
通过 uname -a
命令查看当前的系统信息,可以看到内核版本号为 5.4.0-40-generic。
因此,我们基于
/usr/src/linux-headers-5.4.0-40-generic
目录编译模块即可。编译命令如下:
格式:make -C linux-header目录 M=源码目录 modules
示例:make -C /usr/src/linux-headers-5.4.0-40-generic M=/home/que/quehehe modules
前提条件是在源码同目录下的有Makefile文件,内容为:
obj-m += helloworld.o
其中的 helloworld.o
根据编译源文件进行更改。
之前踩的坑
之前根据上面的 uname
命令获取到内核信息后,运行 sudo apt-cache search linux-source
查询可以安装的源码,如下图所示,并安装了其中的5.4.0源码。
基于这个源码进行漫长的编译后,得到的模块不能加载,原因是模块的 vermagic
版本与内核的版本不一致,模块的 vermagic
版本为5.4.44,而不是5.4.0,我也不清楚这是什么原因,所以不采用下载源码并在源码中编译。
3. 源码编译
从上面可知,可以不用自己编译源码了,但上面踩了坑,但还是记录一下上面编译源码过程。
编译源码主要有以下几步:
- 切换到源码根目录下
- 运行
make menuconfig
命令,成功后按esc直接退出,得到.config文件 - 执行make命令,等待编译完成
我在初始编译的过程中出现了以下几个问题,基本都是库缺失,缺什么库下载什么库就好了。
问题1:Unable to find the ncurses package.
解决方法:sudo apt install libncurses-dev
问题2:flex: not found
解决方法:sudo apt install flex
问题3:bison: not found
解决方法:sudo apt install bison
问题4:fatal error: openssl/opensslv.h: 没有那个文件或目录
解决方法:sudo apt install libssl-dev
上述的解决方法在Ubuntu 2020中解决了对应问题,如果是其他系统,可能对应库的名字不一样。
解决以上问题后,耐心等待,即可正常编译完成。