天天看點

塊裝置驅動架構分析

1. 塊裝置概念:塊裝置是指隻能以塊為機關進行通路的裝置,塊的大小一般是512個位元組的整數倍。常見的塊裝置包括硬體,SD卡,CD光牒等。</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驅動的設計大為簡化。

塊裝置驅動系統架構:

塊裝置驅動架構分析
塊裝置驅動架構分析

先過一遍流程!額 ,徒手撸驅動代碼這難度還真不是一般大!後邊邊學邊提高吧!

繼續閱讀