1. 程式人生 > >linux驅動之塊裝置驅動

linux驅動之塊裝置驅動

塊裝置驅動的系統架構
塊裝置驅動的系統架構

VFS
是對各種具體檔案系統的一種封裝,使用者程式訪問檔案提供統一的介面。例如:
EXT2,FAT,NFS等
系統架構—Cache
當用戶發起檔案訪問請求的時候,首先回到Cache中定址檔案是否被快取了,如果在Cache,則直接從cache中讀取。如果資料不在快取中,就必須要到具體的檔案系統中讀取資料了。
Mapping Layer
首先確定檔案系統的block size,然後計算所請求的資料包含多少個block.
呼叫具體檔案系統的函式來訪問檔案的inode結構,確定所請求的資料在磁碟上的地址。
Generic Block Layer


Linux核心把塊裝置看做是由若干個扇區組成的資料空間,上層的讀寫請求在通用塊層被構造成一個或多個bio結構。
I/O Scheduler Layer,I/O排程層負責採用某種演算法(如:電梯排程演算法)將I/O操作進行排序。
電梯排程演算法的基本原則:如果電梯現在朝上運動,如果當前樓層的上方和下方都有請求,則先響應所有上方的請求,然後才向下響應下方的請求;如果電梯向下運動,則剛好相反。

塊設備註冊過程:
1, 註冊裝置塊驅動程式 register_blkdev
2, 初始化請求佇列 blk_init_queue
3, 指明扇區的大小 blk_queue_logical_block_size(dev->queue, sect_size);
4, 申請一個gendisk結構,初始化
5, 註冊塊裝置
block_device_operations —— 操作函式介面
blk_init_queue —— 請求塊裝置佇列操作
gendisk —— 磁碟資訊結構

#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, }; static int __init blk_init(void) { //註冊裝置塊驅動程式 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; /* * 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; //申請一個gendisk結構,初始化 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); out_vfree: if (dev->data) vfree(dev->data); 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);