英文單詞
gen
n. 訊息; 情報; 資料;v. (給...)提供情報(up); 供給內幕訊息;disk
n. 磁碟; 磁碟;gen disk
在磁碟上之前講解了字元驅動程式,這期開始講解塊裝置驅動程式
字元裝置與塊裝置的區別字元裝置驅動:
當應用層讀寫(read()/write())字元裝置驅動時,是按位元組/字元來讀寫資料的,期間沒有任何快取區,因為資料量小,不能隨機讀取資料,例如:按鍵、LED、滑鼠、鍵盤等
塊裝置:
塊裝置是i/o裝置中的一類, 當我們的應用層對該裝置讀寫時,是按扇區大小來讀寫資料的,若讀寫的資料小於扇區的大小,就會需要快取區, 可以隨機讀寫裝置的任意位置處的資料,例如 硬碟,隨身碟,SD卡。
總結:通俗來講就是:塊裝置相當於是多個字元裝置組成
塊裝置結構:段(Segments):由若干個塊組成。是Linux記憶體管理機制中一個記憶體頁或者記憶體頁的一部分。塊 (Blocks): 由Linux制定對核心或檔案系統等資料處理的基本單位。通常由1個或多個扇區組成。(對Linux作業系統而言)扇區(Sectors):塊裝置的基本單位。通常在512位元組到32768位元組之間,預設512位元組重要的結構體定義buffer_head
struct buffer_head { unsigned long b_state; /* buffer state bitmap (see above) */ struct buffer_head *b_this_page;/* circular list of page's buffers */ struct page *b_page; /* the page this bh is mapped to */ sector_t b_blocknr; /* start block number */ size_t b_size; /* size of mapping */ char *b_data; /* pointer to data within the page */ struct block_device *b_bdev; bh_end_io_t *b_end_io; /* I/O completion */ void *b_private; /* reserved for b_end_io */ struct list_head b_assoc_buffers; /* associated with another mapping */ struct address_space *b_assoc_map; /* mapping this buffer is associated with */ atomic_t b_count; /* users using this buffer_head */};
bio
struct bio { sector_t bi_sector; /* device address in 512 byte sectors */ struct bio *bi_next; /* request queue link */ struct block_device *bi_bdev; unsigned long bi_flags; /* status, command, etc */ unsigned long bi_rw; /* bottom bits READ/WRITE, * top bits priority */ unsigned short bi_vcnt; /* how many bio_vec's */ unsigned short bi_idx; /* current index into bvl_vec */ /* Number of segments in this BIO after * physical address coalescing is performed. */ unsigned short bi_phys_segments; /* Number of segments after physical and DMA remapping * hardware coalescing is performed. */ unsigned short bi_hw_segments; unsigned int bi_size; /* residual I/O count */ /* * To keep track of the max hw size, we account for the * sizes of the first and last virtually mergeable segments * in this bio */ unsigned int bi_hw_front_size; unsigned int bi_hw_back_size; unsigned int bi_max_vecs; /* max bvl_vecs we can hold */ struct bio_vec *bi_io_vec; /* the actual vec list */ bio_end_io_t *bi_end_io; atomic_t bi_cnt; /* pin count */ void *bi_private; bio_destructor_t *bi_destructor; /* destructor */};
block_device
struct block_device { dev_t bd_dev; /* not a kdev_t - it's a search key */ struct inode * bd_inode; /* will die */ int bd_openers; struct mutex bd_mutex; /* open/close mutex */ struct semaphore bd_mount_sem; struct list_head bd_inodes; void * bd_holder; int bd_holders;#ifdef CONFIG_SYSFS struct list_head bd_holder_list;#endif struct block_device * bd_contains; unsigned bd_block_size; struct hd_struct * bd_part; /* number of times partitions within this device have been opened. */ unsigned bd_part_count; int bd_invalidated; struct gendisk * bd_disk; struct list_head bd_list; struct backing_dev_info *bd_inode_backing_dev_info; /* * Private data. You must have bd_claim'ed the block_device * to use this. NOTE: bd_claim allows an owner to claim * the same device multiple times, the owner must take special * care to not mess up bd_private for that case. */ unsigned long bd_private;};
gendisk
struct gendisk { int major; /* major number of driver */ int first_minor; int minors; /* maximum number of minors, =1 for * disks that can't be partitioned. */ char disk_name[32]; /* name of major driver */ struct hd_struct **part; /* [indexed by minor] */ int part_uevent_suppress; struct block_device_operations *fops; struct request_queue *queue; void *private_data; sector_t capacity; int flags; struct device *driverfs_dev; struct kobject kobj; struct kobject *holder_dir; struct kobject *slave_dir; struct timer_rand_state *random; int policy; atomic_t sync_io; /* RAID */ unsigned long stamp; int in_flight;#ifdef CONFIG_SMP struct disk_stats *dkstats;#else struct disk_stats dkstats;#endif struct work_struct async_notify;};
block_device_operations
struct block_device_operations { int (*open) (struct inode *, struct file *); int (*release) (struct inode *, struct file *); int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long); long (*unlocked_ioctl) (struct file *, unsigned, unsigned long); long (*compat_ioctl) (struct file *, unsigned, unsigned long); int (*direct_access) (struct block_device *, sector_t, unsigned long *); int (*media_changed) (struct gendisk *); int (*revalidate_disk) (struct gendisk *); int (*getgeo)(struct block_device *, struct hd_geometry *); struct module *owner;};
buffer_head裡面重要的成員結構體:
bio裡面重要的成員結構體:
備註:typedef void (bh_end_io_t)(struct buffer_head *bh, int uptodate);
在塊裝置操作中重要的函式ll_rw_block
ll_rw_block:low-level access to block devices(塊裝置低階訪問)
函式原型:void ll_rw_block(int rw, int nr, struct buffer_head *bhs[])
其呼叫過程:
1,ll_rw_block2,struct buffer_head *bh = bhs[i];3,submit_bh4,struct bio *bio; bio = bio_alloc(GFP_NOIO, 1);5,bio->bi_end_io = end_bio_bh_io_sync;6,bio_get7,submit_bio8,bio_sectors9,generic_make_request(bio);10,__generic_make_request(bio);11,q = bdev_get_queue(bio->bi_bdev);12,return bdev->bd_disk->queue;13,blk_partition_remap(bio);14,ret = q->make_request_fn(q, bio);
編寫一個最簡單的塊裝置驅動程式/* 參考: * drivers\block\xd.c * drivers\block\z2ram.c */#include <linux/module.h>#include <linux/errno.h>#include <linux/interrupt.h>#include <linux/mm.h>#include <linux/fs.h>#include <linux/kernel.h>#include <linux/timer.h>#include <linux/genhd.h>#include <linux/hdreg.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/wait.h>#include <linux/blkdev.h>#include <linux/blkpg.h>#include <linux/delay.h>#include <linux/io.h>#include <asm/system.h>#include <asm/uaccess.h>#include <asm/dma.h>static struct gendisk *ramblock_disk;static request_queue_t *ramblock_queue;static int major;static DEFINE_SPINLOCK(ramblock_lock);#define RAMBLOCK_SIZE (1024*1024)static unsigned char *ramblock_buf;static int ramblock_getgeo(struct block_device *bdev, struct hd_geometry *geo){ /* 容量=heads*cylinders*sectors*512 */ geo->heads = 2; geo->cylinders = 32; geo->sectors = RAMBLOCK_SIZE/2/32/512; return 0;}static struct block_device_operations ramblock_fops = { .owner = THIS_MODULE, .getgeo = ramblock_getgeo,};static void do_ramblock_request(request_queue_t * q){ static int r_cnt = 0; static int w_cnt = 0; struct request *req; //printk("do_ramblock_request %d\n", ++cnt); while ((req = elv_next_request(q)) != NULL) { /* 資料傳輸三要素: 源,目的,長度 */ /* 源/目的: */ unsigned long offset = req->sector * 512; /* 目的/源: */ // req->buffer /* 長度: */ unsigned long len = req->current_nr_sectors * 512; if (rq_data_dir(req) == READ) { //printk("do_ramblock_request read %d\n", ++r_cnt); memcpy(req->buffer, ramblock_buf+offset, len); } else { //printk("do_ramblock_request write %d\n", ++w_cnt); memcpy(ramblock_buf+offset, req->buffer, len); } end_request(req, 1); }}static int ramblock_init(void){ ramblock_disk = alloc_disk(16); ramblock_queue = blk_init_queue(do_ramblock_request, &ramblock_lock); ramblock_disk->queue = ramblock_queue; major = register_blkdev(0, "ramblock"); ramblock_disk->major = major; ramblock_disk->first_minor = 0; sprintf(ramblock_disk->disk_name, "ramblock"); ramblock_disk->fops = &ramblock_fops; set_capacity(ramblock_disk, RAMBLOCK_SIZE / 512); ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL); add_disk(ramblock_disk); return 0;}static void ramblock_exit(void){ unregister_blkdev(major, "ramblock"); del_gendisk(ramblock_disk); put_disk(ramblock_disk); blk_cleanup_queue(ramblock_queue); kfree(ramblock_buf);}module_init(ramblock_init);module_exit(ramblock_exit);MODULE_LICENSE("GPL");