您的位置:首页 > 其它

块设备驱动之内存模拟硬盘

2017-06-17 10:45 369 查看
转载请注明出处:http://blog.csdn.net/ruoyunliufeng/article/details/25240899

一.块设备驱动框架

app: open,read,write "hello.txt"

--------------------------------------------- 文件的读写

文件系统: vfat, ext2, ext3, yaffs2, jffs2 (把文件的读写转换为扇区的读写)

-----------------ll_rw_block----------------- 扇区的读写(临时先不分析)

1. 把"读写"放入队列

2. 调用队列的处理函数(优化/调顺序/合并)

块设备驱动程序

---------------------------------------------

硬件: 硬盘,flash

与字符设备相比什么差别:

1.块设备仅仅能以块为单位接受输入和返回输出。而字符设备则以字节为单位。大多数设备是字符设备,由于它们不须要缓冲并且不以固定块大小进行操作。

2.块设备对于I/O请求有相应的缓冲区。因此它们能够选择以什么顺序进行响应,字符设备无需缓冲且被直接读写。对于存储设备而言调整读写的顺序作用巨大,由于在读写连续的扇区比分离的扇区更快。

3.符设备仅仅能被顺序读写,而块设备能够随机訪问。尽管块设备可随机訪问,可是对于磁盘这类机械设备而言。顺序地组织块设备的訪问能够提高性能。

二.驱动代码

/* 參考:
* drivers\block\xd.c
* drivers\block\z2ram.c
*/

#include <linux/module.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>

static struct gendisk *ramblock_disk;
static request_queue_t *ramblock_queue;

static int major;

static DEFINE_SPINLOCK(ramblock_lock);

#define RAMBLOCK_SIZE (1024*1024)
static unsigned char *ramblock_buf;

/*几何属性*/
static int ramblock_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
/* 容量=heads*cylinders*sectors*512 */
geo->heads     = 2;                       //多少面
geo->cylinders = 32;                      //多少环
geo->sectors   = RAMBLOCK_SIZE/2/32/512;  //一环有多少扇区
return 0;
}

static struct block_device_operations ramblock_fops = {
.owner	= THIS_MODULE,                     //一个指向拥有该结构的模块指针。通常设为 THIS_MODULE
.getgeo	= ramblock_getgeo,                 //几何属性
};

static void do_ramblock_request(request_queue_t * q)
{
static int r_cnt = 0;
static int w_cnt = 0;
struct request *req;

//printk("do_ramblock_request %d\n", ++cnt);

while ((req = elv_next_request(q)) != NULL)    //一旦有请求req就不为null
{
/* 传输数据三要素: 源,目的,长度 */
/* 源/目的: */
unsigned long offset = req->sector * 512;    //从哪開始(偏移值)

/* 目的/源: */
// req->buffer

/* 长度: */
unsigned long len = req->current_nr_sectors * 512;  //须要传送的扇区数

if (rq_data_dir(req) == READ)  //假设是读
{
//printk("do_ramblock_request read %d\n", ++r_cnt);
memcpy(req->buffer, ramblock_buf+offset, len);  //将内存中数据复制到buffer中
}
else                           //假设是写
{
//printk("do_ramblock_request write %d\n", ++w_cnt);
memcpy(ramblock_buf+offset, req->buffer, len);   //将buffer中的数据拷到内存中
}

end_request(req, 1);
}
}

static int ramblock_init(void)        //入口函数
{
/* 1. 分配一个gendisk结构体 */
ramblock_disk = alloc_disk(16); /* 次设备号个数: 分区个数+1 */

/* 2. 设置 */
/* 2.1 分配/设置队列: 提供读写能力 */
ramblock_queue = blk_init_queue(do_ramblock_request, &ramblock_lock);
ramblock_disk->queue = ramblock_queue;    //把队列放入结构体

/* 2.2 设置其它属性: 比方容量 */
major = register_blkdev(0, "ramblock");  /* 向内核注冊块设备。假设是0,则分配一个新的主设备号给设备。并将设备号返回给调用者cat /proc/devices */
ramblock_disk->major       = major;                 //主设备号
ramblock_disk->first_minor = 0;                    //次设备号
sprintf(ramblock_disk->disk_name, "ramblock");      //名字
ramblock_disk->fops        = &ramblock_fops;        //操作函数
set_capacity(ramblock_disk, RAMBLOCK_SIZE / 512);  //容量

/* 3. 硬件相关操作 */
ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL); //分配一块内存

/* 4. 注冊 */
add_disk(ramblock_disk);        //一点调用它,磁盘设备将被“激活”,并随时会调用它提供方法

return 0;
}

static void ramblock_exit(void)         //出口函数
{
unregister_blkdev(major, "ramblock"); //注销块设备驱动程序
del_gendisk(ramblock_disk);           //卸载磁盘
put_disk(ramblock_disk);
blk_cleanup_queue(ramblock_queue);

kfree(ramblock_buf);
}

module_init(ramblock_init);
module_exit(ramblock_exit);

MODULE_LICENSE("GPL");


三.驱动分析

我们的驱动程序要实现的功能就是将内存模拟硬盘进行操作。

1. 分配gendisk结构体

2. 设置

2.1 分配/设置队列,并将它放入结构体中,用来提供读写能力

2.2 设置gendisk其它信息(主设备号、次设备号、名字、操作函数、容量)

操作函数:这里面主要调用了ramblock_getgeo函数用来设置硬盘的属性(尽管是RAM模拟的,为了使用 老的工具还是要假装设置一下)

3.硬件相关的操作(就是分配一块内存。比較简单)

4. 注冊: add_disk

4.请求处理函数(块设备驱动的核心)

当内核须要驱动程序处理读取、写入以及其它的操作时。就会调用改函数。

static void do_ramblock_request(request_queue_t * q)
{
static int r_cnt = 0;
static int w_cnt = 0;
struct request *req;

//printk("do_ramblock_request %d\n", ++cnt);

while ((req = elv_next_request(q)) != NULL)    //一旦有请求req就不为null
{
/* 传输数据三要素: 源,目的,长度 */
/* 源/目的: */
unsigned long offset = req->sector * 512;    //从哪開始(偏移值)

/* 目的/源: */
// req->buffer

/* 长度: */
unsigned long len = req->current_nr_sectors * 512;  //须要传送的扇区数

if (rq_data_dir(req) == READ)  //假设是读
{
//printk("do_ramblock_request read %d\n", ++r_cnt);
memcpy(req->buffer, ramblock_buf+offset, len);  //将内存中数据复制到buffer中
}
else                           //假设是写
{
//printk("do_ramblock_request write %d\n", ++w_cnt);
memcpy(ramblock_buf+offset, req->buffer, len);   //将buffer中的数据拷到内存中
}

end_request(req, 1);
}
}


这段代码首先推断是否有请求,假设有依据传输数据三要素进行设置,然后推断读写,进行对应的操作。

參考:韦东山视频第二期

LDD3
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: