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

转载请注明出处: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

时间: 2024-11-03 21:35:48

块设备驱动之内存模拟硬盘的相关文章

Linux块设备驱动

推荐书:<Linux内核源代码情景分析> 1.字符设备驱动和使用中等待某一事件的方法①查询方式②休眠唤醒,但是这种没有超时时间③poll机制,在休眠唤醒基础上加一个超时时间④异步通知,异步通知实际上就是发信号⑤输入子系统,这样比较通用 2.块设备相对于字符设备驱动逻辑的变化①对于硬盘对读写的优化假如要读磁头0的扇区0,然后写磁头1的扇区0,然后读磁头0的扇区1,若像字符设备那样,就会机械山跳转2次,效率低.优化:先不执行,放入队列,优化后再执行,这里的优化是指调整顺序. ②对于flash假如要

LINUX块设备驱动&lt;2&gt;

转自:http://blog.chinaunix.net/uid-15724196-id-128140.html 第2章 +---------------------------------------------------+|                 写一个块设备驱动                  |+---------------------------------------------------+| 作者:赵磊                               

LINUX块设备驱动&lt;5&gt;

转自:http://blog.chinaunix.net/uid-15724196-id-128143.html 第5章 +---------------------------------------------------+|                 写一个块设备驱动                  |+---------------------------------------------------+| 作者:赵磊                               

块设备驱动框架详解

一.正确的理解块设备驱动的概念 1.块设备和字符设备的差异 (1)块和字符是两种不同的访问设备的策略 (2)同一个设备可以同时支持块和字符两种访问策略 (3)设备本身的物理特性决定了哪一种访问策略更适合 (4)块设备本身驱动层支持缓冲区,而字符设备驱动层没有缓冲 (5)块设备驱动最适合存储设备 2.块设备驱动的特点 (1)字符设备只能顺序访问(如串口发送数据顺序),而块设备可以随机访问(不连续块访问). (2)传统的机械式块设备(如硬盘.DVD)虽然可以随机访问,但是连续访问效率更高,因此块设备

LINUX块设备驱动&lt;12/13/14/15&gt;

第 12章 +---------------------------------------------------+ |                 写一个块设备驱动                   | +---------------------------------------------------+ | 作者:赵磊                                         | | email: [email protected]             

【转】 bio 与块设备驱动

原文地址: bio 与块设备驱动 系统中能够随机访问固定大小数据片(chunk)的设备被称作块设备,这些数据片就称作块.块设备文件都是以安装文件系统的方式使用,此也是块设备通常的访问方式.块设备的访问方式是随机的,也就是可以在访问设备时,随意的从一个位置跳转到另一个位置.块设备的访问位置必须能够在介质的不同区间前后移动. 所以事实上内核不必提供一个专门的子系统来管理字符设备,但是对块设备的管理却必须要有一个专门的提供服务的子系统.块设备中,最小的可寻址单元是扇区.扇区大小一般是2的整数倍,而最常

linux块设备驱动---相关结构体(转)

上回最后面介绍了相关数据结构,下面再详细介绍 块设备对象结构 block_device 内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区.如果该结构代表一个分区,则其成员bd_part指向设备的分区结构.如果该结构代表设备,则其成员bd_disk指向设备的通用硬盘结构gendisk 当用户打开块设备文件时,内核创建结构block_device实例,设备驱动程序还将创建结构gendisk实例,分配请求队列并注册结构block_device实例. 块设备对象结构blo

linux块设备驱动---概念与框架(转)

基本概念   块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性读到缓冲区. 字符设备(Character device) ---是一个顺序的数据流设备,对这种设备的读写是按字符进行的,而且这些字符是连续地形成一个数据流.他不具备缓冲区,所以对这种设备的读写是实时的. 扇区(Sectors):任何块设备硬件对数据处理的基本单位.通常,1个扇区的大小为512byt

写一个块设备驱动5,6

http://blogold.chinaunix.net/u3/108239/showart.php?id=2144628 第5章 +---------------------------------------------------+|                 写一个块设备驱动                  |+---------------------------------------------------+| 作者:赵磊