4月「掘金·日新计划」第24天
三、块设备驱动
3.1、概念
- 块设备驱动支持缓冲区,字符不支持
- 块和字符是2种不同访问策略 同一设备可以同时支持块和字符访问 设备物理特性,觉得更适合那种访问 块设备驱动更适合存储设备
- 块设备可以随机访问,字符只能顺序访问 nand,sd卡随机等同于顺序效率;硬盘等顺序效率高
- 扇区(sector)块的本身特性,512倍数 块(block)内核对文件系统处理基本单位,多个扇区 段(section)多个块组成 页(page)映射管理基本单位,虚拟内存映射 应用层对块访问,直接操作/dev/block,/dev/sda
3.2、框图
- 虚拟文件系统vfs,把各种文件系统变成一类方便操作
- 结构体 request,对设备的操作读写 request_queue,操作队列 bio,管理一个请求 gendisk,表示一个磁盘设备或分区
3.3、实践
-
查看驱动 lsmod ls /dev cat /proc/devices cat /proc/partitions
-
格式化,挂载 mkfs.ext2 /dev/xxx mount -t ext2 /dev/xxx /xxx unmount /xxx 之后正常操作目录
-
驱动代码
-
使用第一种实现路线 虚拟出来的设备
-
安装
-
register_blkdev 注册主设备驱动
-
实例化 gendlisk 设备结构体
- 结构体里有 主设备号,等待队列,operation结构体,dev下的文件名,等
-
实例化 request_queue 等待队列
- blk_init_queue 参数1,回调函数,处理request请求 参数2,自旋锁,会提供给等待队列
-
set_capacity 设置设备容量
-
add_disk 注册
-
-
处理request回调函数 blk_fetch_queue 从队列中取出一个请求 然后判断是读还是写请求,操作硬件
-
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/genhd.h>
#include <linux/hdreg.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/dma.h>
#define RAMBLOCK_SIZE (1024*1024) // 1MB,2048扇区
static struct gendisk *my_ramblock_disk; // 磁盘设备的结构体
static struct request_queue *my_ramblock_queue; // 等待队列
static DEFINE_SPINLOCK(my_ramblock_lock);
static int major;
static unsigned char *my_ramblock_buf; // 虚拟块设备的内存指针
static void do_my_ramblock_request(struct request_queue *q)
{
struct request *req;
static int r_cnt = 0; //实验用,打印出驱动读与写的调度方法
static int w_cnt = 0;
req = blk_fetch_request(q);
while (NULL != req)
{
unsigned long start = blk_rq_pos(req) *512;
unsigned long len = blk_rq_cur_bytes(req);
if(rq_data_dir(req) == READ)
{
// 读请求
memcpy(req->buffer, my_ramblock_buf + start, len); //读操作,
printk("do_my_ramblock-request read %d times\n", r_cnt++);
}
else
{
// 写请求
memcpy( my_ramblock_buf+start, req->buffer, len); //写操作
printk("do_my_ramblock request write %d times\n", w_cnt++);
}
if(!__blk_end_request_cur(req, 0))
{
req = blk_fetch_request(q);
}
}
}
static int blk_ioctl(struct block_device *dev, fmode_t no, unsigned cmd, unsigned long arg)
{
return -ENOTTY;
}
static int blk_open (struct block_device *dev , fmode_t no)
{
printk("11111blk mount succeed\n");
return 0;
}
static int blk_release(struct gendisk *gd , fmode_t no)
{
printk("11111blk umount succeed\n");
return 0;
}
static const struct block_device_operations my_ramblock_fops =
{
.owner = THIS_MODULE,
.open = blk_open,
.release = blk_release,
.ioctl = blk_ioctl,
};
static int my_ramblock_init(void)
{
major = register_blkdev(0, "my_ramblock");
if (major < 0)
{
printk("fail to regiser my_ramblock\n");
return -EBUSY;
}
// 实例化
my_ramblock_disk = alloc_disk(1); //次设备个数 ,分区个数 +1
//分配设置请求队列,提供读写能力
my_ramblock_queue = blk_init_queue(do_my_ramblock_request, &my_ramblock_lock);
//设置硬盘属性
my_ramblock_disk->major = major;
my_ramblock_disk->first_minor = 0;
my_ramblock_disk->fops = &my_ramblock_fops;
sprintf(my_ramblock_disk->disk_name, "my_ramblcok"); // /dev/name
my_ramblock_disk->queue = my_ramblock_queue;
set_capacity(my_ramblock_disk, RAMBLOCK_SIZE / 512);
/* 硬件相关操作 */
my_ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL);
add_disk(my_ramblock_disk); // 向驱动框架注册一个disk或者一个partation的接口
return 0;
}
static void my_ramblock_exit(void)
{
unregister_blkdev(major, "my_ramblock");
del_gendisk(my_ramblock_disk);
put_disk(my_ramblock_disk);
blk_cleanup_queue(my_ramblock_queue);
kfree(my_ramblock_buf);
}
module_init(my_ramblock_init);
module_exit(my_ramblock_exit);
MODULE_LICENSE("GPL");