当前课程知识点:Linux 内核分析与应用 > 第9章 设备驱动 > 9.9 工程实践-块设备驱动程序实现(下) > Video
大家好我们在上一期视频节目中给大家介绍了块
设备子系统它
常用的数据结构和常用的接口的API函数
那我们在那次视频节目中和
给大家留了一个实验
这个实验里我们要求大家
写一个
内核驱动
在这个内核驱动里面
里我们使用ramdisk
就是系统的内存
来构造一个虚拟的块设备
那在这个块设备里我们做一些
读写操作和对
这个request的一些简单操作
这个实验
对大家理解块设备是非常有帮助的
那我们这期节目中
就和大家简单过一遍实验的我们一个
示例的代码
我们首先打开ramdisk_
driver.c这个文件
那这个文件一共155行其实这个
也不长我们来看下第
151行这里module_init
这里面就指示了驱动的入口
它的入口函数是my_init
那我们来看下my_init这个函数
第97行这里
我们首先简单给这个
ramdisk
设定一个大小这里我们
定义了ramdisk 256M
接下来在第100行这里我们使用这个
vmalloc这个内核的
分配内存的接口函数来给
ramdisk块设备来
分配存储空间
那104行这里我们使用这个
blk_init_queue这个函数
来为我们这个
块设备来创造一个
请求队列
request_queue
这个blk_init_queue
这里面有两个参数
第一个参数是一个函数指针my_request
这是一个函数我们一会分析
那第二个参数是lock
是一个锁所以我们在第98行里面我们去
初始化了一个spin_lock_init的锁
那这个函数会返回一个请求队列
那这个请求队列我们定义在哪里呢
这个函数我们初始化在
第19行它实际上是struct
request_queue这样一个数据结构
这个数据结构我们在上期节目中
和大家介绍过了
那我们来看
109行这里我们给这个
request_queue来设置它的sector_size
那它的sector_size我们这里
简单把它定义到512个字节
那我们再看一下第111行这里
register_blkdev这个函数去
注册一个
块设备那我们这里第一个参数
等于0的话就是让系统主动去分配一个
主设备号
那第二个参数是
这个设备它的名称
这个注册成功以后它会返回一个
主设备号那这个主设备号我们在这里
把它写成mybdrv
_ma_no
那我们看下第119行这里
我们这里用一个alloc_disk
来去分配一个gendisk
一个通用的
disk的一个描述符
我们看下my_gd它是
定义在第16行它是一个
struct gendisk这么一个数据结构
那这个数据结构
分配完之后我们要对他的一些
主要成员做一些初始化
看126行这里
它的major这里就指
指向我们刚才用register
blkdev这个函数它去成功分配出来的这个
块设备的这个主设备号
然后first minor我们就设成零
然后fops我们就
指向我们驱动里面我们自己定义的一个
fopsfops实现在哪实际在90行这里
它实际上是一个block_device_operations
那这个block_device_operations
它是非常类似我们之前讲的
字符设备它的一个
file_opereations的一个方法集
那在这个
驱动里面我们这个方法集
只实现了一个ioctl的这么一个
一个方法
第129行就是
来给这个
gendisk来起一个名字
然后第130行这里就是把我们刚才
分配的my_request_queue
就是这个gendisk的queue这个成员
要指向我们刚才
分配的这个
请求队列第131行这里就是设置这个
块设备它的一个capacity
它的容量是多少
那这些
主要成员都
配置好了之后我们就可以使用
add_disk这个函数来
把gendisk给
像Linux内核去注册就是说把gendisk
添加到Linux系统的块设备子系统里面
这些做完之后我们会在这里加一个打印
会把我们这个块设备它的主设备号给打出来
然后把我们ram disk它的容量也打出来
这个就是驱动的init函数
它所做的工作那
接下来我们来看一下另外一个非常重要的一个操作就是我们刚才讲了
块设备它有一个非常重要的一个处理就是去处理
这个请求队列里面的这些请求
那这个请求队列里面的请求
他是怎么处理的呢
我们刚才在104行里面讲了这里我们去
初始化一个请求队列的时候 我们
的第一个参数是一个函数指针也就是
对这个request的一个实际处理的一个函数
也就是我们这里的my_request这个函数
它实现在第22行这里
我们来分析一下这个函数在28行这里我们会
加一个打印
告诉我告诉大家我们现在已经进来这里要处理这个request
那第30行这里
blk_fetch_request
就是从这个请求队列里面去拉一个请求出来
拉一个request出来
然后第31行就是要去
处理这些请求
我们第32行这里
nr_sectors
其实说这个请求我们需要多少个sector
第33行这里blk_rq_
pos也就说这个请求它是在相对于这个
块设备他是在哪个opposite里面pos
s代表opposite的意思
所以第35行因为我们的块设备是一个简单的一个
连续内存作为存储的一个虚拟的一个块设备 那所以
我们就可以通过一个简单的一个
计算能算出当前这个request
他的请求它的存储地址是在哪里
然后能够简单地算出来request它的
size是多少
我们第38行做一个简单的一个判断
你请求的这个
大小不能超过ram disk
的最大对吧然后第43行这里我们就
因为这个请求它有分是读还是写
那第43到46行这里我们处理它是一个
写的一个请求这里我们也加了一个打印
这里会把sector和nr_sectors会打印出来 然后
第46行这里我们做一个拷贝因为我们这个是
以系统内存作为存储介质的所以我们
简单做个拷贝把我们的请求的数据
这里数据用bio_data请求的数据
直接拷贝到我们ptr指针里面
那接下来这个48到50行也是类似的这里我们
处理read请求同样也是用memcpy
做一个简单的一个操作
这个整个流程就是对一个
从文件系统来的一个请求
对于我们一个块设备驱动它是怎么做的
当然这个请求是非常简单了
这个处理非常简单接下来我们看一下另外一个操作就是
我们在block_device_operations里面
在这个方法集里面我们定义一个ioctl
我们这里实现了一个my_ioctl
那我们看一下my_ioctl里面根据我们的实验的要求我们需要
实现HDIO_GETGEO
.这么一个ioctl的请求
在ioctl请求里面
其实我们就是去把这个
块设备的一些参数你比如说他有多少个
磁头多少个扇区然后有多少柱面这些信息打印出来
在我们这个实验里面我们只是
简单的做一个假的这么一个数据
然后最终是通过copy_to_user
这么一个API把这些数据信息
传到用户空间里面这个就是我们整个的
实验的代码其实我们
最重要的还是要去理解我们
上一次视频讲了一些这个块设备驱动里面核心的这些API的一些使用
接下来我们来编译一下这个驱动
我现在是在ubuntu 18.04的
系统里面去编译驱动模块
我使用的内核版本是4.15的内核
我们要编译首先可以写一个简单的Makefile
这个是简单的Makefile
我们直接使用make的命令
就可以去编译内核模块
编译完之后我们发现在这里面一个ramdisk_driver.ko
这时候我们可以使用
sudo insmod
ramdisk_driver.ko来加载驱动
他说这里已经加载过了我们先把它卸载了
我们使用sudo insmod
ramdisk_driver.ko来加载这个模块大家看到我们已经加载了
这时候我们可以用
dmesg来看一下
那dmesg看到这里面
它会打印这个
device successfully
然后我们可以看到它的主设备号是252
然后它的ram disk的容量是256MB
对吧 那接下来我们来看一下这个
我们创建的设备是在哪里
我们可以用ls /dev/
大家可以看到这里面有一个myramdisk
这个myramdisk就是我们创建的块设备
我们可以看一下我们的driver是怎么写的
您看到我们这里有一个我们的MY_DEVICE_NAME
起的名字就是myramdisk
这时候我们可以
ramdisk给格式化了
我们用sodo mkfs
ext4
然后dev/myramdisk
大家看到我们
对它格式化的时候我们可以看到我们的一些信息 对不对
格式化完之后我们可以对它进行mount操作
我们首先来看一下
dmesg里面打印的信息
大家看到dmesg里面打印了很多的信息
你比如说
比如说你看以这个为例
我们每一次request处理的时候我们
入口我们会打印一个start handle request
你看这里面都是writing对不对writing at
sector这是一个就是说这是一个
这是一个sector的一个 它在整个
ramdisk的一个起始的一个指针就是相当于文件的一个指针
然后这个是这次操作他操作多少个sector
接下来我们可以对它进行一个
mount的一个动作我们mount到mnt这里面OK
那我们可以进入到mnt的目录里面
大家看到我们已经mount这个了我们可以用df
我们可以用df我们可以用df命令来看一下我们系统挂载的
分区大家看到dev/myramdisk
挂载到mnt里面OK
先把这个清掉
这时候我们可以在这里创建一个文件夹
我们创一个mytest文件夹OK
mytest文件夹这时候我们再看一下
dmesg那这个dmesg的信息就是我
敲这个mkdir mytest
创建目录的时候他发起的一些
块设备的一些IO操作
大家看一下我们在这里已经创建了mytest
然后这些是他
我们这个块设备驱动里面
它处理这些
来自文件系统的请求他所做的一些
最底层的一些操作了
-1.1 Linux操作系统概述
-1.2 Linux内核结构以及内核模块编程
--Video
-1.3 Linux内核源码中的双链表结构
--Video
-1.4 源码分析-内核中的哈希表
--Video
-1.5 动手实践-Linux内核模块的插入和删除
--Video
-第1章 概述--章节测验
-2.1 内存管理之内存寻址
--Video
-2.2 段机制
--Video
-2.3分页机制
--Video
-2.4 动手实践-把虚拟地址转换成物理地址
--Video
-第2章 内存寻址--章节测验
-3.1 进程概述
--Video
-3.2 Linux进程创建
--Video
-3.3 Linux进程调度
--Video
-3.4 动手实践-打印进程描述符task_struct中的字段
--Video
-3.5工程实践-基于内核模块的负载监控
--Video
-第3章 进程管理--章节测验
-4.1 Linux内存管理机制
--Video
-4.2 进程用户空间管理机制
--Video
-4.3 物理内存分配与回收机制(上)
--Video
-4.4 物理内存分配与回收机制(下)
--Video
-4.5 动手实践-Linux内存映射基础(上)
--Video
-4.6 动手实践-Linux内存映射实现(中)
--Video
-4.7 动手实践-Linux内存映射测试(下)
--Video
-4.8 初学者对内存管理的常见疑惑
-第4章 内存管理--章节测验
-5.1 中断机制概述
--Video
-5.2 中断处理机制
--Video
-5.3 中断下半部处理机制
--Video
-5.4 时钟中断机制
--Video
-5.5 动手实践-中断上半部的代码分析及应用
--Video
-5.6 动手实践-中断下半部的代码分析及应用
--Video
-第5章 中断--章节测验
-6.1 Linux中的各种API
--Video
-6.2 系统调用机制
--Video
-6.3 动手实践-添加系统调用(系统调用日志收集系统)
--Video
-第6章 系统调用--章节测验
-7.1 内核同步概述
--Video
-7.2 内核同步机制
--Video
-7.3 动手实践-内核多任务并发实例(上)
--Video
-7.4 动手实践-内核多任务并发实例(下)
--Video
-第7章 内核同步--章节测验
-8.1 虚拟文件系统的引入
--Video
-8.2 虚拟文件系统的主要数据结构
--Video
-8.3 文件系统中的各种缓存
--Video
-8.4 页高速缓存机制以及读写
--Video
-8.5 动手实践-编写一个文件系统(上)
--Video
-8.6 动手实践-编写一个文件系统(中)
--Video
-8.7 动手实践-编写一个文件系统(下)
--Video
-第8章 文件系统--章节测验
-9.1 设备驱动概述
--Video
-9.2 I/O空间管理
--Video
-9.3 设备驱动模型
--Video
-9.4 字符设备驱动程序简介
--Video
-9.5 块设备驱动程序简介
--Video
-9.6 动手实践-编写字符设备驱动程序
--Video
-9.7工程实践-编写块设备驱动的基础(上)
--Video
-9.8 工程实践-块设备驱动程序分析(中)
--Video
-9.9 工程实践-块设备驱动程序实现(下)
--Video
-第9章 设备驱动--章节测验
-致谢与说明
--Video