Linux I/O Block--块设备的表示


块设备的特点是其平均访问时间较长,因此为了提高块设备的访问效率,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:起始次设备号

minors:描述了该块设备有多少个次设备号,或者说有多少个分区,如果minors为1,则表示该块设备没有分区

part_tbl:整个块设备的分区信息都包含在里面,其核心结构是一个struct hd_struct的指针数组,每一项都指向一个描述分区的hd_struct结构

fops:指向特定于设备的底层操作函数集

queue:块设备的请求队列,所有针对该设备的请求都会放入该请求队列中,经过I/O scheduler的处理再进行提交

  • 1
  • 2
  • 3
  • 下一页

相关内容