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