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驱动的设计大为简化。
块设备驱动系统架构:
先过一遍流程!额 ,徒手撸驱动代码这难度还真不是一般大!后边边学边提高吧!