日期:2014-05-16 浏览次数:20753 次
块设备的特点是其平均访问时间较长,因此为了提高块设备的访问效率,Linux内核用了很多的笔墨来设计和块设备相关的部分,这样一来,从代码的角度来看,访问一个文件的过程变得尤其的漫长……整个路径包含的过程基本可以概括为虚拟文件系统-->块设备实际文件系统-->通用块层-->I/O scheduler-->块设备驱动程序。为了提高块设备的访问效率,内核主要是在两个方面下功夫:
1.引入缓存,当用户空间要访问文件时,内核不可能每次都去访问块设备,内核会将块设备的内容读取到内存中,以便下次访问时可以直接在内存中找到相应的内容,这其中又涉及到了预读等相关的问题,当然这不是现在关注的重点……
2.对于I/O请求的重排列,I/O请求并不会立即被响应,而是会放在一个队列里进行一段延迟,以期能够和后来的I/O请求进行合并或者进行排序。因为像磁盘这样的块设备,其耗时主要是因为磁头的定位,因此内核会尽量保证磁头只往一个方向移动,而不是来回移动(可以和电梯的运作进行对比),简而言之,就是将存储介质上相邻的数据请求安排在一起,对于I/O请求的处理主要包括合并和排序,具体如何处理,由I/O scheduler决定。
首先,我们先来了解一个块设备是如何表示的。描述块设备的数据结构有两个,一个是struct block_device,用来描述一个块设备或者块设备的一个分区;另一个是struct gendisk,用来描述整个块设备的特性。对于一个包含多个分区的块设备,struct block_device结构有多个,而struct gendisk结构永远只有一个。
struct block_device { dev_t bd_dev; /* not a kdev_t - it's a search key */ struct inode * bd_inode; /* will die */ struct super_block * bd_super; int bd_openers; struct mutex bd_mutex; /* open/close mutex */ 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; /* * 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; /* The counter of freeze processes */ int bd_fsfreeze_count; /* Mutex for freeze */ struct mutex bd_fsfreeze_mutex; };
bd_dev:该设备(分区)的设备号
bd_inode:指向该设备文件的inode
bd_openers:一个引用计数,记录了该块设备打开的次数,或者说有多少个进程打开了该设备
bd_contains:如果该block_device描述的是一个分区,则该变量指向描述主块设备的block_device,反之,其指向本身
bd_part:如果该block_device描述的是一个分区,则该变量指向分区的信息
bd_part_count:如果是分区,该变量记录了分区被打开的次数,在进行分区的重新扫描前,要保证该计数值为0
bd_disk:指向描述整个设备的gendisk结构
struct gendisk {
/* major, first_minor and minors are input parameters only,
* don't use directly. Use disk_devt() and disk_max_parts().
*/
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[DISK_NAME_LEN]; /* name of major driver */
char *(*devnode)(struct gendisk *gd, mode_t *mode);
/* Array of pointers to partitions indexed by partno.
* Protected with matching bdev lock but stat and other
* non-critical accesses use RCU. Always access through
* helpers.
*/
struct disk_part_tbl *part_tbl;
struct hd_struct part0;
const struct block_device_operations *fops;
struct request_queue *queue;
void *private_data;
int flags;
struct device *driverfs_dev; // FIXME: remove
struct kobject *slave_dir;
struct timer_rand_state *random;
atomic_t sync_io; /* RAID */
struct work_struct async_notify;
#ifdef CONFIG_BLK_DEV_INTEGRITY
struct blk_integrity *integrity;
#endif
int node_id;
};
major:块设备的主设备号
first_minor:起始次设备号