实验目的
1、 熟悉 linux 下驱动程序设计
2、 了解 linux 下字符设备驱动设计
实验要求
一、内核编译提供以下实验的环境
二、ubantu14.04 32 位下第一个 hello world 驱动程序
编写驱动程序时,首先必须建立内核源码树,即下载源码后,执行 make 编译后的形式,它是编译驱动的前提。ubuntu 系统默认情况下是没有的。先前的内核只需要有一套内核头文件就够了,内核模块编译时需要内核源码树中的目标文件,通过这种方式,可得到一个更加健壮的模块装载器,但也需要这些目标文件存在于内核目录树中。
三、 ubantu14.04 第二个 memory 驱动程序
四、ubantu14.04 第三个使用文件私有数据的 globalmem 的设备
实验环境
Pc电脑一台 ,学校机房C203,17号电脑(Linux系统)
内核编译教程
Linux设备驱动指导
实验步骤、过程
(含原理图、流程图、关键代码,或实验过程中的记录、数据等)
实验步骤
一、内核编译提供以下实验的环境
- uname -r 查看内核版本 ,输出: 4.10.0-19-generic
如果安装系统时,自动安装了源码。在 /usr/src 目录下有对应的使用的版本目录,ubuntu 系统默认情况下是没有的,如下所示:
- ls /usr/src,输出:linux-headers- 4.10.0-19,linux-headers- 4.10.0-19-generic 。
- 查看一下可一下载的源码包(切记不要使用超级用户使用此命令否则……会提示没有此命令)执行:apt-cache search linux-source,输出: linux-source-4.10.0-19 - Linux kernel source for version 4.10.0-19 with Ubuntu patches 。
- 执行:sudo apt-get install linux-source-4.10.0-19
下载完成后,在/usr/src 下会有压缩包,然后解压,解压后生成一个新的目录/usr/src/linux-source-4.10.0-19
开始配置内核 选择最快的原版的配置(默认)方式 。
- 执行:make oldconfig
当然你也可以使用 自己喜欢的配置方式 如 menuconfig , xconfig(必须有 GTK 环境吧)。
- 执行:make bzImage
执行结束后,可以看到在当前目录下生成了一个新的文件: vmlinux 。
- 执行:make modules 和 make modules_install,执行结束之后,会在/lib/modules 下生成新的目录/lib/modules/3.13.0-32-generic/,若由于主机本身内核版本就为 3.13.0-32-generic,所以/lib/modules/3.13.0-32-generic/本身就存在,所以 make modules 和 make modules_install 两步就不需要执行了。至此,构造内核源码树完成。【注意:如果没有建立内核源码树,按下面步骤,虽然能够生成 hello.ko,但执行 sudo insmod hello.ko 后,执行 lsmod 会没反应,导致系统报告问题,会导致下次开机或重启时有问题,若启动不了,可以进入 recovery 模式,执行 fsck,开机做嵌入式 linux 开发一般在 PC 机上编译好了,下到板子上去运行,板子上的 linux 内核和 PC 机上的 linux 版本很多时候都是不一样的,比如 pc 机上的是 linux2.6,板子上的是 linux3.1,这个时候就要下 linux3.1 的内核,用它编译的驱动模块在板子上才能加载上,不然会出错。】
二、ubantu14.04 32 位下第一个 hello world 驱动程序
- c 文件以及 Makefile 文件
- 到hello.c文件目录下执行make,会生成hello.ko文件以及其他相关文件。
- 执行sudo insmod .hell.ko 加载模块
- 执行lsmod就可以看到hello模块了
- sudo rmmod hello
三、 ubantu14.04 第二个 memory 驱动程序
- 编写mydm1.c
- 编写 Makefile文件
- 编写test.c文件
- 运行:
①到包含 Makefile 和 mydm1.c 的目录下执行 make,生成 mydm1.ko;
②执行 sudo insmod mydm1.ko;
③验证:lsmod | grep mydm1
④需要创建一个文件(该设备文件用于和设备驱动操作)mknod /dev/fgj c 224 0 c 代表字符设备 224为主设备号,0 为从设备号
⑤ gcc test.c,然后执行:sudo ./a.out 输出如下:open/dev/fgjsuccessfully ,write successfully。
四、ubantu14.04 第三个使用文件私有数据的 globalmem 的设备
- 编写globalmem.c代码如下。
- 编写 Makefile文件。
- 执行:make ,然后 sudo insmod globalmem.ko。
- 验证:lsmod 可以看到该模块,mknod /dev/globalmem c 354 0, echo ‘good global’ > /dev/globalmem, cat /dev/globalmem 可以看到输出 good global; 还出现了:cat: /dev/globalmem: 没有那个设备或地址,目前不知道是什么问题。
- 测试:输出如下: open/dev/globalmem successfully ,write successfully ,read successfully:mem。
程序说明及实现
一、内核编译提供以下实验的环境
- uname -r查看内核版本。如图1所示。
图1 内核版本
2. ls /usr/src,输出。如图2所示。
图2 查看src目录
3. 查看一下可一下载的源码包(切记不要使用超级用户使用此命令否则……会提示没有此命令)执行:apt-cache search linux-source。因为机房电脑的电脑已经存在了编译好的内核因此这一步可以跳过。如图3所示。 图3查看源码包(因为apt缓存没有)
图3 查看源码包(因为apt缓存没有)
4. 执行:sudo apt-get install linux-source-4.10.0-19。如图4所示。 图4 sudo apt-get install结果图
图4 sudo apt-get install结果图
- 执行:make oldconfig 。当然你也可以使用 自己喜欢的配置方式 如 menuconfig , xconfig(必须有GTK环境吧),如图5所示。之后,使用“ll”查看一下目录,如图6所示。。并且查看一下“.config”文件。如图7所示。
图5 执行“make config”
图6 查看目录”
图7 “.config”文件”
- 执行:make bzImage。执行结束后,可以看到在当前目录下生成了一个新的文件: vmlinux。如图8所示。
图8 查看增加的vmlinux
- 执行:make modules和make modules_install,执行结束之后,会在/lib/modules下生成新的目录/lib/modules/3.13.0-32-generic/,若由于主机本身内核版本就为3.13.0-32-generic,所以/lib/modules/3.13.0-32-generic/本身就存在,所以make modules和make modules_install两步就不需要执行了。
- 内核编译结束,环境准备工作完成。
二、ubantu14.04 32 位下第一个 hello world 驱动程序
- 新建一个文件夹“test1”用来存放hello world 驱动程序相关文件。
- 编写hello.c文件以及Makefile文件。源代码实验报告已经提供,因此使用“vi”+文件名的方式创建文件并且将源代码copy进入到文件中,创建两个文件如图9所示。
图9 创建两个文件
① hello.c文件。源代码如图10所示。
图10 hello.c文件源代码
② Makefile文件。源代码如图11所示。
图11 Makefile文件源代码
对Makefile文件的编写进行简单解释:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
这句是Makefile的规则:这里的$ (MAKE)就相当于make,-C 选项的作用是指将当前工作目录转移到你所指定的位置。“M=”选项的作用是,当用户需要以某个内核为基础编译一个外部模块的话,需要在make modules 命令中加入“M=dir”,程序会自动到你所指定的dir目录中查找模块源码,将其编译,生成ko文件
注意:$ (MAKE) -C $ (KERNELDIR) M=$ (PWD) modules和$ (MAKE) -C $ (KERNELDIR) M=$ (PWD) modules_install前面为tab占位符。
- 在test1目录下执行“make”命令。效果如图12所示。执行“make”命令之后,查看当前目录下的文件,如图13所示。
图12 执行“make”效果
图13 test1目录下文件
- 执行sudo insmod ./hello.ko加载模块。
- 执行lsmod就可以看到hello模块。如图14所示。
图14 加载hello模块
- 执行“cat /var/log/syslog | grep world查看输出”。如图15所示。
图15 第一个hello world驱动程序最终效果图
三、 ubantu14.04 第二个 memory 驱动程序
- 新建一个文件夹“test2”用来存放memory 驱动程序相关文件。
- 编写mydm1.c文件以及Makefile文件。源代码实验报告已经提供,因此使用“vi”+文件名的方式创建文件并且将源代码copy进入到文件中,创建两个文件
如图16所示。
图16 创建两个文件
- 编写mydm1.c文件。如图17所示。
图17 mydm1.c文件源代码
- 编写 Makefile文件。如图18所示。
图18 Makefile文件源代码
- 创建并编写test.c文件用于测试。如图19所示。
图19 test.c文件源代码
- 运行:
①到包含 Makefile 和 mydm1.c 的目录下执行 make,生成 mydm1.ko,如图20所示。并且使用“ll”查看当前目录可以看到相关文件,如图21所示。
图20 执行“make”命令效果
图21 test2目录
②执行“sudo insmod mydm1.ko”加载模块并执行“lsmod”查看加载情况,如图22,图23所示。
图22 加载“mydm1”模块
图23 查看是否加载“mydm1”模块
③验证:“lsmod | grep mydm1”输出如图24所示。
图24 验证结果
④需要创建一个文件(该设备文件用于和设备驱动操作)“mknod /dev/fgj c 224 0 ”代表字符设备 224为主设备号,0 为从设备号。如图25所示。
图25 创建设备驱动相关文件
⑤“gcc test.c”,如图26所示。然后执行:“sudo ./a.out ”输出如图27所示。
图26 “gcc test.c”编译test.c文件
图27 执行“a.out”文件效果
四、ubantu14.04 第三个使用文件私有数据的 globalmem 的设备
- 新建一个文件夹“test3”用来存放globalmem 驱动程序相关文件。
- 编写globalmem.c文件以及Makefile文件。源代码实验报告已经提供,因此使用“vi”+文件名的方式创建文件并且将源代码copy进入到文件中,创建两个文件如图28所示。
图28 创建两个文件
- 编写globalmem.c文件。如图29所示。
图29 globalmem.c文件源代码
- 编写 Makefile文件。如图30所示。
图30 Makefile文件源代码
- 到包含 Makefile 和 globalmem.c 的目录下执行 make,生成 globalmem.ko,如图31所示。
图31 执行“make”命令
- 执行“sudo insmod globalmem.ko”加载模块,如图32所示。并且查看是否已加载成功,如图33所示。
图32 执行加载模块
图33 查看模块globalmem是否加载成功
- 验证:分别执行 “mknod /dev/globalmem c 354 0”;“echo ‘hello global’ > /dev/globalmem”;“cat /dev/globalmem” 可以看到输出“hello global”(自己改写了内容); 还出现了:cat: /dev/globalmem: 没有那个设备或地址,目前不知道是什么问题。如图34所示。
图34 验证结果
- 测试:
① 创建并编写test.c文件用于测试。如图35,图36所示。
图35 创建test.c文件
图36 test.c文件源代码
② 执行“gcc test.c”编译test.c文件,如图37所示。然后执行:“sudo ./a.out ”输出如图38所示。
图37 编译test.c文件
图38 最终执行结果
实验结果或总结
(对实验结果进行相应分析,或总结实验的心得体会,并提出实验的改进意见)
实验结果:
一、 内核编译提供以下实验的环境
实验室已经有提供编译好的内核,因为本人将编译内核的流程大致进行了一遍。
二、 ubantu14.04 32 位下第一个 hello world 驱动程序
图39 第一个hello world驱动程序最终效果图
三、 ubantu14.04 第二个 memory 驱动程序
图40 第二个 memory 驱动程序最终效果图
四、 ubantu14.04 第三个使用文件私有数据的 globalmem 的设备
图41 第三个使用文件私有数据的 globalmem 的设备最终效果图
将本实验用到的“test1,test2,test3”三个文件夹的目录结构展示如图42,图43,图44。
图42 test1文件夹目录结构
图43 test2文件夹目录结构
图44 test3文件夹目录结构
建议
提供可以编译内核虚拟机环境,这样可以节省不少时间。
心得体会
通过这次可以总结编写Linux系统下的驱动程序步骤:①建立Linux的驱动骨架(装载和卸载Linux驱动)任何类型的程序都有一个基本结构,linux驱动程序也不例外。Linux内核在使用驱动时首先需要装载驱动。在装载过程中也需要进行一些初始化的工作。②注册和注销设备文件任何一个linux驱动都需要一个设备文件,否则应用程序将无法与驱动程序交互。③指定与驱动相关的信息驱动程序是自描述的。④指定回调函数一个驱动程序并不一定要指定所有的回调函数。⑤编写业务逻辑⑥编写Makefile文件⑦编写Linux驱动程序⑦安装和卸载Linux驱动。基本了解了Linux系统下的驱动程序编写方法,对驱动程序有了更加深刻的认识。
在刚开始接触驱动程序时,遇到了很多很多的问题,当时就特别想放弃,好在我及时的寻求了同学的帮助。同学很热心的帮助我解答问题,我这才慢慢找到了驱动程序的“套路”。通过本次实验,我同样学到了Linux系统下“make”命令的用法。起初,我对“make”知之甚少,只是听说过,但是从来没有用过。直到这次实验,我才认识到它的强大用处。“make”是用来编译的,它从Makefile中读取指令,然后进行编译。它的强大之处就在于不仅可以用于编译众多互相关联的源代码文件,而且还可以编译内核模块。