块设备驱动架构分析

1. 块设备概念:块设备是指只能以块为单位进行访问的设备,块的大小一般是512个字节的整数倍。常见的块设备包括硬件,SD卡,光盘等。</span>

上边是通过一个编写好的块设备驱动,然后安装块设备驱动以及一些相关操作来体会块设备驱动!(此处省略)

2. 块设备驱动的系统架构

2.1 系统架构---VFS

VFS是对各种具体文件系统的一种封装,用户程序访问文件提供统一的接口。

2.2 系统架构---Cache

当用户发起文件访问请求的时候,首先回到Disk Cache中寻址文件是否被缓存了,如果在Cache,则直接从cache中读取。如果数据不在缓存中,就必须要到具体的文件系统中读取数据了。

2.3 Mapping Layer

2.3.1 首先确定文件系统的block size,然后计算所请求的 数据包含多少个block.

2.3.2 调用具体文件系统的函数来访问文件的inode结构,确定所请求的数据在磁盘上的地址。

2.4 Generic Block Layer

Linux内核把块设备看做是由若干个扇区组成的数据空间,上层的读写请求在通用块层被构造成一个或多个bio结构。

2.5 I/O Scheduler Layer I/O调度层负责采用某种算法(如:电梯调度算法)将I/O操作进行排序。

电梯调度算法的基本原则:如果电梯现在朝上运动,如果当前楼层的上方和下方都有请求,则先响应所有上方的请求,然后才向下响应下方的请求;如果电梯向下运动,则刚好相反。

2.6 块设备驱动

在块系统架构的最底层,由块设备驱动根据排序好的请求,对硬件进行数据访问。

块设备驱动流程分析:

1.6 注册块设备---add_disk

2. 实现读写请求函数

2.1 取出一个要处理的请求---blk_fetch_request

2.2 更具请求里的信息访问硬件,获取数据

2.3 利用__blk_end_request_cur判读请求队列里是否还有剩余的请求要处理,如果有按照1、2来处理

一个最简单的块设备驱动程序:

#include<linux/module.h>
#include<linux/init.h>
#include<linux/blkdev.h>
#include<linux/bio.h>

#include <linux/sched.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h>   /* kmalloc() */
#include <linux/fs.h>   /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/timer.h>
#include <linux/types.h> /* size_t */
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/hdreg.h> /* HDIO_GETGEO */
#include <linux/kdev_t.h>
#include <linux/vmalloc.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/buffer_head.h> /* invalidate_bdev */
#include <linux/bio.h>

static int major = 0;

static int sect_size = 512;//定义每个扇区的大小为512字节
static int nsectors = 1024;//扇区数目

struct blk_dev{
	int size;
	u8 *data;
	struct request_queue *queue;
	struct gendisk *gd;
};

struct blk_dev *dev;

static struct block_device_operations blk_ops = {
	.owner = THIS_MODULE,
};
//static void blk_transfer(struct blk_dev *dev, unsigned long sector,unsigned long nsect, char *buffer, int write)
static void blk_transfer(struct blk_dev *dev, unsigned long sector, unsigned long nsect, char *buffer, int write)
{
	unsigned long offset = sector * sect_size;
	unsigned long nbytes = nsect * sector;

	if(write)//如果是写操作 将用户空间的数据写到磁盘上去
		memcpy(dev->data + offset, buffer, nbytes);

	else
		memcpy(buffer, dev->data + offset, nbytes);

}

static void blk_request(struct request_queue *q)
{
	struct request *req;//保存取出的请求

	req = blk_fetch_request(q);//从请求队列中取出一个请求

	while(req != NULL)
	{
		//处理该请求

		//操作的起始扇区 请求操作扇区的数目 数据读出来放到哪里, 或写数据来自哪里
		blk_transfer(dev, blk_rq_pos(req), blk_rq_cur_sectors(req), req->buffer, rq_data_dir(req));
		//blk_transfer(dev, blk_rq_pos(req), blk_rq_cur_sectors(req), req->buffer, rq_data_dir(req));

		if( !__blk_end_request_cur(req, 0) )//判读如果不是最后一个请求
		    req = blk_fetch_request(q);//再去取一个请求
	}
}

void setup_device(void)
{
	dev->size = nsectors * sect_size;

	dev->data = vmalloc(dev->size);

	dev->queue = blk_init_queue(blk_request, NULL);//初始化请求队列

	blk_queue_logical_block_size(dev->queue, sect_size);//设置扇区尺寸

	dev->gd = alloc_disk(1);//分配块设备结构

	dev->gd->major = major;
	dev->gd->first_minor = 0;
	dev->gd->fops = &blk_ops;
	dev->gd->queue = dev->queue;
	dev->gd->private_data = dev;
	sprintf(dev->gd->disk_name, "simp_blk%d", 0);//设备名为simp_blk0
	set_capacity(dev->gd, nsectors);//设置扇区数

	add_disk(dev->gd);

}

int blk_init(void)
{
	//两个参数 第一个参数为0表示为动态分配设备号 返回主设备号
	major = register_blkdev(0, "blk");//注册块设备驱动程序

	if( major <= 0 )
	{
		printk("register blk dev fail!\n");

		return -EBUSY;
	}

	dev = kmalloc(sizeof(struct blk_dev),GFP_KERNEL);

	setup_device();

	return 0;
}

void blk_exit(void)
{
	del_gendisk(dev->gd);

	blk_cleanup_queue(dev->queue);

	vfree(dev->data);

	unregister_blkdev(major, "blk");

	kfree(dev);
}

module_init(blk_init);
module_exit(blk_exit);

编译安装 格式化成ext3的时候虚拟机直接重启了!原来驱动的BUG动不动就把系统搞挂!

这个是可以运行的:

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>

#include <linux/sched.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h>   /* kmalloc() */
#include <linux/fs.h>   /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/timer.h>
#include <linux/types.h> /* size_t */
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/hdreg.h> /* HDIO_GETGEO */
#include <linux/kdev_t.h>
#include <linux/vmalloc.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/buffer_head.h> /* invalidate_bdev */
#include <linux/bio.h>

MODULE_LICENSE("Dual BSD/GPL");

static int major = 0;

static int sect_size = 512;

static int nsectors = 1024; 

/*
* The internal representation of our device.
*/
struct blk_dev{
         int size;                        /* Device size in sectors */
         u8 *data;                        /* The data array */
         struct request_queue *queue;     /* The device request queue */
         struct gendisk *gd;              /* The gendisk structure */
};

struct blk_dev *dev;

/*
* Handle an I/O request, in sectors.
*/
static void blk_transfer(struct blk_dev *dev, unsigned long sector,
   unsigned long nsect, char *buffer, int write)
{
unsigned long offset = sector*sect_size;
unsigned long nbytes = nsect*sect_size;

if ((offset + nbytes) > dev->size) {
   printk (KERN_NOTICE "Beyond-end write (%ld %ld)\n", offset, nbytes);
   return;
}
if (write)
   memcpy(dev->data + offset, buffer, nbytes);
else
   memcpy(buffer, dev->data + offset, nbytes);
}

/*
* The simple form of the request function.
*/
static void blk_request(struct request_queue *q)
{
struct request *req;

req = blk_fetch_request(q);
while (req != NULL) {
   struct blk_dev *dev = req->rq_disk->private_data;

   blk_transfer(dev, blk_rq_pos(req), blk_rq_cur_sectors(req), req->buffer, rq_data_dir(req));

   if(!__blk_end_request_cur(req, 0))
   {
    req = blk_fetch_request(q);
   }
}
}

/*
* Transfer a single BIO.
*/
static int blk_xfer_bio(struct blk_dev *dev, struct bio *bio)
{
int i;
struct bio_vec *bvec;
sector_t sector = bio->bi_sector;

/* Do each segment independently. */
bio_for_each_segment(bvec, bio, i) {
   char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
   blk_transfer(dev, sector, bio_cur_bytes(bio)>>9 /* in sectors */,
    buffer, bio_data_dir(bio) == WRITE);
   sector += bio_cur_bytes(bio)>>9; /* in sectors */
   __bio_kunmap_atomic(bio, KM_USER0);
}
return 0; /* Always "succeed" */
}

/*
* Transfer a full request.
*/
static int blk_xfer_request(struct blk_dev *dev, struct request *req)
{
struct bio *bio;
int nsect = 0;

__rq_for_each_bio(bio, req) {
   blk_xfer_bio(dev, bio);
   nsect += bio->bi_size/sect_size;
}
return nsect;
}

/*
* The device operations structure.
*/
static struct block_device_operations blk_ops = {
.owner            = THIS_MODULE,
};

/*
* Set up our internal device.
*/
static void setup_device()
{
/*
* Get some memory.
*/
dev->size = nsectors*sect_size;
dev->data = vmalloc(dev->size);
if (dev->data == NULL) {
   printk (KERN_NOTICE "vmalloc failure.\n");
   return;
}

   dev->queue = blk_init_queue(blk_request, NULL);
   if (dev->queue == NULL)
    goto out_vfree;

blk_queue_logical_block_size(dev->queue, sect_size);
dev->queue->queuedata = dev;
/*
* And the gendisk structure.
*/
dev->gd = alloc_disk(1);
if (! dev->gd) {
   printk (KERN_NOTICE "alloc_disk failure\n");
   goto out_vfree;
}
dev->gd->major = major;
dev->gd->first_minor = 0;
dev->gd->fops = &blk_ops;
dev->gd->queue = dev->queue;
dev->gd->private_data = dev;
sprintf (dev->gd->disk_name, "simp_blk%d", 0);
set_capacity(dev->gd, nsectors*(sect_size/sect_size));
add_disk(dev->gd);
return;

out_vfree:
if (dev->data)
   vfree(dev->data);
}

static int __init blk_init(void)
{
/*
* Get registered.
*/
major = register_blkdev(major, "blk");
if (major <= 0) {
   printk(KERN_WARNING "blk: unable to get major number\n");
   return -EBUSY;
}

dev = kmalloc(sizeof(struct blk_dev), GFP_KERNEL);
if (dev == NULL)
   goto out_unregister;

   setup_device();

return 0;

out_unregister:
unregister_blkdev(major, "sbd");
return -ENOMEM;
}

static void blk_exit(void)
{

   if (dev->gd) {
    del_gendisk(dev->gd);
    put_disk(dev->gd);
   }
   if (dev->queue)
    blk_cleanup_queue(dev->queue);
   if (dev->data)
    vfree(dev->data);

   unregister_blkdev(major, "blk");
kfree(dev);
}

module_init(blk_init);
module_exit(blk_exit);

makefile:

ifneq ($(KERNELRELEASE),)

obj-m := simple-blk.o

else

KDIR := /lib/modules/2.6.32-279.el6.i686/build
all:
	make -C $(KDIR) M=$(PWD) modules
clean:
	rm -f *.ko *.o *.mod.o *.mod.c *.symvers

endif

这个块设备驱动的测试上面也有步骤!重点是理解块设备驱动的大体流程!对块设备驱动又个大体印象!

下面来介绍一个和下面即将要出场的flash驱动相关的知识!

MTD

MTD设备体验:Flash在嵌入式系统中是必不可少,它是bootloader、linux内核和文件系统的最佳载体。在linux内核中引入了MTD子系统为NOR FLASH和NAND FLASH设备提供统一的接口,从而使得FLASH驱动的设计大为简化。

块设备驱动系统架构:

先过一遍流程!额 ,徒手撸驱动代码这难度还真不是一般大!后边边学边提高吧!

时间: 2024-08-25 14:59:17

块设备驱动架构分析的相关文章

块设备驱动框架分析(二)

参考:块设备驱动之一  块设备驱动之二  块设备驱动之三 总结上一篇的块设备驱动的步骤: 1. 分配gendisk: alloc_disk static struct gendisk * ramblock_disk = alloc_disk(16); /* 次设备号个数: 分区个数+1 */2. 设置2.1 分配/设置队列: // 它提供读写能力static struct request_queue  * ramblock_queue = blk_init_queue(do_ramblock_r

Linux块设备驱动详解

<机械硬盘> a:磁盘结构 -----传统的机械硬盘一般为3.5英寸硬盘,并由多个圆形蝶片组成,每个蝶片拥有独立的机械臂和磁头,每个堞片的圆形平面被划分了不同的同心圆,每一个同心圆称为一个磁道,位于最外面的道的周长最长称为外道,最里面的道称为内道,通常硬盘厂商会将圆形蝶片最靠里面的一些内道(速度较慢,影响性能)封装起来不用:道又被划分成不同的块单元称为扇区,每个道的周长不同,现代硬盘不同长度的道划分出来的扇区数也是不相同的,而磁头不工作的时候一般位于内道,如果追求响应时间,则数据可存储在硬盘的

【深入Linux块设备驱动层次之一】整体层次

最近在做文件系统和linux块设备相关的工作,需要对文件系统和底层块设备之间的关系和交互有比较深入的了解.笔者参考的书籍有敖青云所著的<存储技术原理分析-基于Linux2.6内核原代码>,同时参考的还有Jonathan Corbet.Alessandro Rubini和Greg Kroah-Hartman 一起著作的linux设备经典书籍<Linux设备驱动Edition 3 >.陈学松写的<深入Linux设备驱动内核机制>.对比了一下,还是敖青云写的很存储结合更紧密一

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

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

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

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

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

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

块设备驱动之NAND FLASH驱动程序

转载请注明出处:http://blog.csdn.net/ruoyunliufeng/article/details/25240909 一.框架总结 二.硬件原理 相比于nor flash,我们可以清楚的看出引脚少了很多,主要是输入输出引脚进行了复用.现在我说下各引脚的用途. a.LDATA0~LDATA7这8个引脚为输入输出引脚.命令.地址.数据的传输都是由这8个引脚实现的(引脚复用,节约引脚). b.RnB:此引脚用来判忙.因为命令.数据.地址发出去和收到时候不能立刻就完成,需要一个时间.此

块设备驱动程序设计

一.块设备简介 1.块设备 块设备将数据存储在固定大小的块中,每个块的大小通常在512字节到32768字节之间.磁盘.SD卡都是常见的块设备. 2.块设备VS字符设备 # 块设备和字符设备最大的区别在于读写数据的基本单元不同.块设备读写数据的基本单元为块,例如磁盘通常为一个sector,而字符设备的基本单元为字节. # 块设备能够随机访问,而字符设备则只能顺序访问. 块设备体系架构: VFS是对各种具体文件系统的一种封装,为用户程序访问文件提供统一的接口. Disk Cache 当用户发起文件访

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

转载请注明出处:http://blog.csdn.net/ruoyunliufeng/article/details/25240899 一.块设备驱动框架 app:      open,read,write "hello.txt" ---------------------------------------------  文件的读写 文件系统: vfat, ext2, ext3, yaffs2, jffs2      (把文件的读写转换为扇区的读写) ----------------