本文所有内容基于内核版本Linux-v3.2.40。

    块设备的基本概念这里就不赘述了,本文的主要目的是分析和讨论块设备所涉及的几个重要的结构体以及他们之间的关系。

1. 块设备的上层抽象 - block_device

    block_device是伪文件系统bdevfs中对块设备或设备分区的抽象,它唯一的对应于一个设备号(对分区来说,主设备号相同,次设备号不同)。
    它的详细内容如下(include/linux/fs.h):

点击(此处)折叠或打开

  1. struct block_device {
  2.     dev_t            bd_dev; /* 对应底层设备的设备号 */
  3.     int            bd_openers; /* 该设备同时被多少进程打开 */
  4.     struct inode *        bd_inode;    /* 块设备的inod,可利用bd_dev通过bdget获得 */
  5.     struct super_block *    bd_super; /* 文件系统的超级块信息 */
  6.     struct mutex        bd_mutex;    /* open/close mutex */
  7.     struct list_head    bd_inodes
  8.     void *            bd_claiming;
  9.     void *            bd_holder;
  10.     int            bd_holders;
  11.     bool            bd_write_holder;
  12. #ifdef CONFIG_SYSFS
  13.     struct list_head    bd_holder_disks;
  14. #endif
  15.     /* 首先block_device既可以是gendisk的抽象,又可以是hd_struct(分区)的抽象
  16.      * 当作为分区的抽象时,bd_contains指向了该分区所属的gendisk对应的block_device
  17.      * 当作为gendisk的抽象时,bd_contains指向自身的block_device
  18.      */
  19.     struct block_device *    bd_contains;
  20.     unsigned        bd_block_size; /* 块的大小 */
  21.     struct hd_struct *    bd_part; /* 指向分区指针,对于gendisk,指向内置的分区0 */
  22.     /* number of times partitions within this device have been opened. */
  23.     unsigned        bd_part_count; /* 该设备的所有分区同时被打开的次数 */
  24.     int            bd_invalidated; /* 置1表示内存中的分区信息无效,下次打开设备时需要重新扫描分区表 */
  25.     struct gendisk *    bd_disk; /* 通用磁盘抽象,当该block_device作为分区抽象时,指向该分区所属的gendisk,当作为gendisk的抽象时,指向自身 */
  26.     struct list_head    bd_list;
  27.     /*
  28.      * Private data. You must have bd_claim'ed the block_device
  29.      * to use this. NOTE: bd_claim allows an owner to claim
  30.      * the same device multiple times, the owner must take special
  31.      * care to not mess up bd_private for that case.
  32.      */
  33.     unsigned long        bd_private;

  34.     /* The counter of freeze processes */
  35.     int            bd_fsfreeze_count;
  36.     /* Mutex for freeze */
  37.     struct mutex        bd_fsfreeze_mutex;
  38. };

2. 通用磁盘描述 - gendisk

    gendisk是对通用磁盘的一个描述,与真正的底层物理设备相关联。其详细内容如下(include/linux/genhd.h):    

点击(此处)折叠或打开

  1. struct gendisk {
  2.     /* major, first_minor and minors are input parameters only,
  3.      * don't use directly. Use disk_devt() and disk_max_parts().
  4.      */
  5.     int major;            /* 主设备号 */
  6.     int first_minor; /* 第一个次设备号 */
  7.     /* 表示分区的个数,分区号从1开始,0表示gendisk本身 */
  8.     int minors; /* maximum number of minors, =1 for
  9.                                          * disks that can't be partitioned. */

  10.     char disk_name[DISK_NAME_LEN];    /* 磁盘的名称,用于在sysfs和/proc/partitions中表示该磁盘 */
  11.     char *(*devnode)(struct gendisk *gd, mode_t *mode);

  12.     unsigned int events;        /* supported events */
  13.     unsigned int async_events;    /* async events, subset of all */

  14.     /* Array of pointers to partitions indexed by partno.
  15.      * Protected with matching bdev lock but stat and other
  16.      * non-critical accesses use RCU. Always access through
  17.      * helpers.
  18.      */
  19.     struct disk_part_tbl __rcu *part_tbl; /* 分区表 */
  20.     struct hd_struct part0; /* 用于表示gendisk本身 */

  21.     const struct block_device_operations *fops; /* 指向底层具体设备的操作函数,一般由用户驱动程序实现,如ramdisk驱动实现的fops为brd_fops */
  22.     struct request_queue *queue; /* 该disk关联的请求队列 */
  23.     void *private_data; /* 私有数据,用于提供给用户驱动程序使用 */

  24.     int flags;
  25.     struct device *driverfs_dev; // FIXME: remove
  26.     struct kobject *slave_dir;

  27.     struct timer_rand_state *random;
  28.     atomic_t sync_io;        /* RAID */
  29.     struct disk_events *ev;
  30. #ifdef CONFIG_BLK_DEV_INTEGRITY
  31.     struct blk_integrity *integrity;
  32. #endif
  33.     int node_id;
  34. };

3. 磁盘分区描述 - hd_struct

    hd_struct用于描述一个具体的磁盘分区,其详细内容如下(include/linux/genhd.h):

点击(此处)折叠或打开

  1. struct hd_struct {
  2.     sector_t start_sect; /* 该分区的起始扇区号 */
  3.     sector_t nr_sects; /* 该分区的扇区个数,也就是分区容量 */
  4.     sector_t alignment_offset;
  5.     unsigned int discard_alignment;
  6.     struct device __dev;
  7.     struct kobject *holder_dir;
  8.     int policy, partno; /* 该分区的分区号 */
  9.     struct partition_meta_info *info;
  10. #ifdef CONFIG_FAIL_MAKE_REQUEST
  11.     int make_it_fail;
  12. #endif
  13.     unsigned long stamp;
  14.     atomic_t in_flight[2];
  15. #ifdef    CONFIG_SMP
  16.     struct disk_stats __percpu *dkstats;
  17. #else
  18.     struct disk_stats dkstats;
  19. #endif
  20.     atomic_t ref;
  21.     struct rcu_head rcu_head;
  22. };

4. 三者之间的关系

    如果你能读到我这篇文章,说明你应该很熟悉软件开发中的管理模式,下面我就类比软件开发的管理模式来说明三者之间的关系,可能不是十分贴切,但希望对你了解三者的关系能提供一些帮助。
    block_device就相当于每个程序猿的档案信息(如姓名、电话、邮件、职位以及leader等等),gendisk相当于一个项目组,而hd_struct相当于项目组中的每一个程序猿。如何解释呢?^_^
    想象一下,对于人力管理者(相当于VFS)来说,他其实并不关心底下干活的是哪个程序猿,是高富帅还是矮矬穷,是美女还是帅哥,他只需要知道你的档案信息(block_device)就可以了,因为只要有了你的档案信息,在需要你的时候就随时可以找到你(程序猿就是这么悲催)。
    而对于一个项目组(gendisk)来说,里面一个或多个程序猿(hd_struct)。因为项目组至少有一个leader吧,而leader本质上也是一个程序猿(相当于struct hd_struct part0)。当一个项目比较庞大时,可能一个leader会带领多个兄弟(就像一个硬盘管理着多个分区),然而如果是一个迷你项目,可能只需要项目组leader一个人就搞定了(就像一个磁盘不进行分区)。
    是不是有那么点意思?^_^
    当人力管理需要找某个程序猿(hd_struct或者part0)时,只需要找到他的档案信息(block_device)就可以了,因为二者是一对一的关系,而且根据程序猿找到他的项目组(gendisk)是不是也是一件很容易的事情?同理,一旦找到了项目组(gendisk),那么里面的所有程序猿(hd_struct)是不是也非常明朗了?总之一句话,档案信息(block_device)充当了人力管理(相当于VFS)和项目组成员(gendisk、hd_struct)之间的桥梁。
    不知道你有没有看明白,如果还有些混乱直接看下面的图好了:

块设备剖析之关键数据结构分析 - block_device/gendisk/hd_struct-LMLPHP

    本文如有不妥或错误的地方欢迎批评指正,大家一起交流讨论,共同进步。
    本文乃原创文章,请勿随意转载,如需转载请详细标明转载出处。
10-10 13:24