块设备
- [1. 块设备驱动](#1. 块设备驱动)
-
- [1.1 块设备驱动---驱动框架](#1.1 块设备驱动---驱动框架)
1.
块设备驱动相比字符设备驱动的主要区别如下:
①、块设备只能以块为单位进行读写,块是 linux 虚拟文件系统(VFS)基本的数据传输单位。字符设备是以字节为单位进行数据传输的,不需要缓冲
②、块设备在结构上是可以进行随机访问的,对于这些设备的读写都是按块进行的,块设备使用缓冲区来暂时存放数据,等到条件成熟以后再一次性将缓冲区中的数据写入块设备中。这么做的目的为了提高块设备寿命,大家如果仔细观察的话就会发现有些硬盘或者 NAND Flash 就会标明擦除次数(flash 的特性,写之前要先擦除),比如擦除 100000 次等。因此,为了提高块设备寿命引入了缓冲区,数据先写入到缓冲区中,等满足一定条件后再一次性写入到真正的物理存储设备中,这样就减少了对块设备的擦除次数,提高了块设备寿命
1.1 ---驱动框架
module_init(xxx_init)向 Linux 内核注册一个模块加载函数(xxx_init),当使用"insmod"命令加载驱动时,xxx_init 函数就会被调用1.1
kzalloc()申请用于ramdisk内存1.2
register_blkdev()向 Linux 内核注册块设备block_device1.3
alloc_disk()分配并初始化磁盘ramdisk.gendisk1.4
blk_init_queue()分配并初始化请求队列ramdisk.queue,并指定请求处理函数xxx_request_fn()1.4.1
blk_fetch_request()循环处理请求队列中的每个请求1.4.2 读写 bio 中的数据缓冲区来读写磁盘
1.4.3
__blk_end_request_cur()处理完请求队列中的所有请求1.5
add_disk()将申请并初始化好的磁盘ramdisk.gendisk添加到内核中
module_exit(xxx_exit)向 Linux 内核注册一个模块卸载函数(xxx_exit),当使用"rmmod"命令卸载驱动时, xxx_exit 函数就会被调用2.1
del_gendisk()删除磁盘ramdisk.gendisk2.2
put_disk()减少磁盘ramdisk.gendisk 的引用计数2.3
blk_cleanup_queue()删除请求队列ramdisk.queue2.4
unregister_blkdev()向 Linux 内核注销块设备2.5
kfree()释放申请ramdisk内存添加模块 LICENSE 信息和作者信息
3.1 MODULE_LICENSE("GPL");
3.2 MODULE_AUTHOR("xiedong");
c#include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/init.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/gpio.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/of_gpio.h> #include <linux/semaphore.h> #include <linux/timer.h> #include <linux/i2c.h> #include <linux/genhd.h> #include <linux/blkdev.h> #include <linux/hdreg.h> #include <asm/mach/map.h> #include <asm/uaccess.h> #include <asm/io.h> #define RAMDISK_SIZE (2 * 1024 * 1024) /* 容量大小为2MB */ /* ramdisk设备结构体 */ struct ramdisk_dev{ int major; // 主设备号 unsigned char *ramdiskbuf; // ramdisk内存空间,用于模拟块设备 spinlock_t lock; // 自旋锁 struct gendisk *gendisk; // gendisk struct request_queue *queue;// 请求队列 }; struct ramdisk_dev ramdisk; /* ramdisk设备 */ /* * @description : 打开块设备 * @param - dev : 块设备 * @param - mode : 打开模式 * @return : 0 成功;其他 失败 */ int ramdisk_open(struct block_device *dev, fmode_t mode) { printk("ramdisk open\r\n"); return 0; } /* * @description : 释放块设备 * @param - disk : gendisk * @param - mode : 模式 * @return : 0 成功;其他 失败 */ void ramdisk_release(struct gendisk *disk, fmode_t mode) { printk("ramdisk release\r\n"); } /* * @description : 获取磁盘信息 * @param - dev : 块设备 * @param - geo : 模式 * @return : 0 成功;其他 失败 */ int ramdisk_getgeo(struct block_device *dev, struct hd_geometry *geo) { /* 这是相对于机械硬盘的概念 */ geo->heads = 2; /* 磁头 */ geo->cylinders = 32; /* 柱面 */ geo->sectors = RAMDISK_SIZE / (2 * 32 *512); /* 一个磁道上的扇区数量 */ return 0; } // 块设备操作函数 static struct block_device_operations ramdisk_fops = { .owner = THIS_MODULE, .open = ramdisk_open, .release = ramdisk_release, .getgeo = ramdisk_getgeo, }; /* * @description : 处理传输过程 * @param-req : 请求 * @return : 无 */ static void ramdisk_transfer(struct request *req) { unsigned long start = blk_rq_pos(req) << 9; /* blk_rq_pos获取到的是扇区地址,左移9位转换为字节地址 */ unsigned long len = blk_rq_cur_bytes(req); /* 大小 */ /* bio中的数据缓冲区 * 读:从磁盘读取到的数据存放到buffer中 * 写:buffer保存这要写入磁盘的数据 */ void *buffer = bio_data(req->bio); if(rq_data_dir(req) == READ) /* 读数据 */ memcpy(buffer, ramdisk.ramdiskbuf + start, len); else if(rq_data_dir(req) == WRITE) /* 写数据 */ memcpy(ramdisk.ramdiskbuf + start, buffer, len); } /* * @description : 请求处理函数 * @param-q : 请求队列 * @return : 无 */ void ramdisk_request_fn(struct request_queue *q) { int err = 0; struct request *req; /* 循环处理请求队列中的每个请求 */ req = blk_fetch_request(q); while(req != NULL) { /* 针对请求做具体的传输处理 */ ramdisk_transfer(req); /* 判断是否为最后一个请求,如果不是的话就获取下一个请求 * 循环处理完请求队列中的所有请求。 */ if (!__blk_end_request_cur(req, err)) req = blk_fetch_request(q); } } static int __init xxx_init(void) { int ret = 0; printk("ramdisk init\r\n"); // 1、申请用于 ramdisk 内存 ramdisk.ramdiskbuf = kzalloc(RAMDISK_SIZE, GFP_KERNEL); if(ramdisk.ramdiskbuf == NULL) { ret = -EINVAL; goto ram_fail; } // 2、初始化自旋锁 spin_lock_init(&ramdisk.lock); // 3、向 Linux 内核注册块设备 ramdisk.major = register_blkdev(0, "ramdisk"); // 由系统自动分配主设备号 if(ramdisk.major < 0) { goto register_blkdev_fail; } printk("ramdisk major = %d\r\n", ramdisk.major); // 4、分配并初始化磁盘 gendisk ramdisk.gendisk = alloc_disk(3); if(!ramdisk.gendisk) { ret = -EINVAL; goto gendisk_alloc_fail; } // 5、分配并初始化请求队列 request_queue ramdisk.queue = blk_init_queue(ramdisk_request_fn, &ramdisk.lock); if(!ramdisk.queue) { ret = EINVAL; goto blk_init_fail; } // 6、将申请并初始化好的 gendisk 添加到内核中 ramdisk.gendisk->major = ramdisk.major; // 主设备号 ramdisk.gendisk->first_minor = 0; // 第一个次设备号(起始次设备号) ramdisk.gendisk->fops = &ramdisk_fops; // 操作函数 ramdisk.gendisk->private_data = &ramdisk; // 私有数据 ramdisk.gendisk->queue = ramdisk.queue; // 请求队列 sprintf(ramdisk.gendisk->disk_name, "ramdisk"); // 名字 set_capacity(ramdisk.gendisk, RAMDISK_SIZE/512); // 设备容量(单位为扇区) add_disk(ramdisk.gendisk); return 0; blk_init_fail: put_disk(ramdisk.gendisk); gendisk_alloc_fail: unregister_blkdev(ramdisk.major, "ramdisk"); register_blkdev_fail: kfree(ramdisk.ramdiskbuf); ram_fail: return ret; } static void __exit xxx_exit(void) { printk("ramdisk exit\r\n"); del_gendisk(ramdisk.gendisk); //1. 删除 gendisk put_disk(ramdisk.gendisk); //2. 减少 gendisk 的引用计数 blk_cleanup_queue(ramdisk.queue); //3. 删除请求队列 request_queue unregister_blkdev(ramdisk.major, "ramdisk"); //4. 向 Linux 内核注销块设备 kfree(ramdisk.ramdiskbuf); //5. 释放申请的内存(模拟块设备) } module_init(xxx_init); module_exit(xxx_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("xiedong");
*[块设备驱动]: 块设备驱动其实就是这些存储设备驱动,比如 SD 卡、EMMC、NAND Flash、Nor Flash、SPI Flash、机械硬盘、固态硬盘等

