linux 块设备驱动(二)——块设备数据结构
本文来源于:
1. http://www.cnblogs.com/dyllove98/archive/2013/07/01/3165567.html
块设备相关的数据结构以及接口:
块设备接口则相对复杂,读写API没有直接到块设备层,而是直接到文件系统层,然后再由文件系统层发起读写请求。
一: block_device: block_device结构代表了内核中的一个块设备。它可以表示整个磁盘或一个特定的分区。当这个结构代表一个分区时,它的bd_contains成员指向包含这个分区的设备,bd_part成员指向设备的分区结构。当这个结构代表一个块设备时,bd_disk成员指向设备的gendisk结构。
struct block_device { dev_t bd_dev; struct inode * bd_inode; /*分区结点*/ int bd_openers; struct semaphore bd_sem; /*打开/关闭锁*/ struct semaphore bd_mount_sem; /* 加载互斥锁*/ struct list_head bd_inodes; void * bd_holder; int bd_holders; struct block_device * bd_contains; unsigned bd_block_size;//分区块大小 struct hd_struct * bd_part; 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; unsigned long bd_private; };
二:gendisk是一个单独的磁盘驱动器的内核表示。内核还使用gendisk来表示分区。
struct gendisk { int major; //主设备号 int first_minor; int minors; //最大的次设备号数量,如果设备不能分区,该值为1 char disk_name[32]; //主设备名 struct hd_struct **part; //分区信息,有minors个 struct block_device_operations *fops;//设备操作 struct request_queue *queue; //设备管理I/O请求 void *private_data; sector_t capacity; int flags; char devfs_name[64]; int number; struct device *driverfs_dev; struct kobject kobj; struct timer_rand_state *random; int policy; atomic_t sync_io; unsigned long stamp, stamp_idle; int in_flight; #ifdef CONFIG_SMP struct disk_stats *dkstats; #else struct disk_stats dkstats; #endif };
gendisk结构的操作函数包括以下几个: struct gendisk *alloc_disk(int minors); //分配磁盘 void add_disk(struct gendisk *disk); //增加磁盘信息 void unlink_gendisk(struct gendisk *disk) //删除磁盘信息 void delete_partition(struct gendisk *disk, int part); //删除分区 void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len, int flags);//添加分区
三: 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; }; block_device_operations并不能完全提供文件操作全部的API,实际上只提供了open、release等函数,其他的文件操作依赖于def_blk_fops:
const struct file_operations def_blk_fops = { .open = blkdev_open, .release = blkdev_close, .llseek = block_llseek, .read = do_sync_read, .write = do_sync_write, .aio_read = generic_file_aio_read, .aio_write= generic_file_aio_write_nolock, .mmap = generic_file_mmap, .fsync = block_fsync, .unlocked_ioctl = block_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = compat_blkdev_ioctl, #endif .splice_read = generic_file_splice_read, .splice_write = generic_file_splice_write, };
四: 系统对块设备进行读写操作时,通过块设备通用的读写操作函数将一个请求保存在该设备的操作请求队列(request queue)中,然后调用这个块设备的底层处理函数,对请求队列中的操作请求进行逐一执行。request_queue结构描述了块设备的请求队列,该结构定义如下:
struct request_queue { struct list_head queue_head; struct request *last_merge; elevator_t elevator; /*请求队列列表*/ struct request_list rq; request_fn_proc *request_fn; merge_request_fn *back_merge_fn; merge_request_fn *front_merge_fn; merge_requests_fn *merge_requests_fn; make_request_fn *make_request_fn; prep_rq_fn *prep_rq_fn; unplug_fn *unplug_fn; merge_bvec_fn *merge_bvec_fn; activity_fn *activity_fn; /*自动卸载状态*/ struct timer_list unplug_timer; int unplug_thresh; unsigned long unplug_delay; /*自动卸载延时*/ struct work_struct unplug_work; struct backing_dev_info backing_dev_info; void *queuedata; void *activity_data; unsigned long bounce_pfn; int bounce_gfp; unsigned long queue_flags;//各种队列标志 /*保护队列结构,避免重入*/ spinlock_t *queue_lock; /* 请求的核心结构*/ struct kobject kobj; /*请求的配置*/ unsigned long nr_requests; /* 请求的最大数*/ unsigned int nr_congestion_on; unsigned int nr_congestion_off; unsigned short max_sectors; unsigned short max_phys_segments; unsigned short max_hw_segments; unsigned short hardsect_size; unsigned int max_segment_size; unsigned long seg_boundary_mask; unsigned int dma_alignment; struct blk_queue_tag *queue_tags; atomic_t refcnt; unsigned int in_flight; /*sg 参数配置*/ unsigned int sg_timeout; unsigned int sg_reserved_size; };
请求队列相关的处理函数包括: //创建队列时提供了一个自旋锁。 request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock); //获得队列中第一个未完成的请求。 struct request *elv_next_request(request_queue_t *q); void end_request(struct request *req, int uptodate);//请求完成 void blk_stop_queue(request_queue_t *queue); //停止请求 void blk_start_queue(request_queue_t *queue); //开始请求 void blk_cleanup_queue(request_queue_t *);//清除请求队列
五:向内核注册和注销一个块设备可使用如下函数:
int register_blkdev(unsigned int major, const char *name); int unregister_blkdev(unsigned int major, const char *name);