块设备的分区信息由struct hd_struct结构描述,其中最重要的信息就是分区的起始扇区号和分区的大小。所有分区信息都一起保存在gendisk的part_tbl结构中,同时每个分区的block_device也可以通过bd_part来查询对应的分区信息。
下图描述了block_device,gendisk以及分区描述符之间的关系(块设备有两个分区)
下面通过打开一个块设备的过程,来理解这些结构之间的联系。
对于块设备文件的操作,通过block_dev伪文件系统来完成,open操作定义的函数为blkdev_open()
blkdev_open的主要任务有两个
1.获取设备的block_device信息
2.从gendisk中读取相关信息保存到block_device,同时建立数据结构之间的联系
static int blkdev_open(struct inode * inode, struct file * filp)
{
struct block_device *bdev;
int res;
/*
* Preserve backwards compatibility and allow large file access
* even if userspace doesn't ask for it explicitly. Some mkfs
* binary needs it. We might want to drop this workaround
* during an unstable branch.
*/
filp->f_flags |= O_LARGEFILE;
if (filp->f_flags & O_NDELAY)
filp->f_mode |= FMODE_NDELAY;
if (filp->f_flags & O_EXCL)
filp->f_mode |= FMODE_EXCL;
if ((filp->f_flags & O_ACCMODE) == 3)
filp->f_mode |= FMODE_WRITE_IOCTL;
bdev = bd_acquire(inode);//获取block device实例
if (bdev == NULL)
return -ENOMEM;
filp->f_mapping = bdev->bd_inode->i_mapping;
res = blkdev_get(bdev, filp->f_mode);//通过gendisk获取信息并建立联系
if (res)
return res;
if (filp->f_mode & FMODE_EXCL) {
res = bd_claim(bdev, filp);
if (res)
goto out_blkdev_put;
}
return 0;
out_blkdev_put:
blkdev_put(bdev, filp->f_mode);
return res;
}
bd_acquire()负责获取block_device的实例
static struct block_device *bd_acquire(struct inode *inode)
{
struct block_device *bdev;
spin_lock(&bdev_lock);
bdev = inode->i_bdev;//如果这个设备之前被打开过则可以直接通过i_bdev获取
if (bdev) {
atomic_inc(&bdev->bd_inode->i_count);
spin_unlock(&bdev_lock);
return bdev;
}
spin_unlock(&bdev_lock);
bdev = bdget(inode->i_rdev);//通过设备号的信息来获取block device实例
if (bdev) {
spin_lock(&bdev_lock);
if (!inode->i_bdev) {
/*
* We take an additional bd_inode->i_count for inode,
* and it's released in clear_inode() of inode.
* So, we can access it via ->i_mapping always
* without igrab().
*/
atomic_inc(&bdev->bd_inode->i_count);
inode->i_bdev = bdev;
inode->i_mapping = bdev->bd_inode->i_mapping;
list_add(&inode->i_devices, &bdev->bd_inodes);
}
spin_unlock(&bdev_lock);
}
return bdev;
}
-------------------------------分割线-------------------------------
struct block_device *bdget(dev_t dev)
{
struct block_device *bdev;
struct inode *inode;
/*这里先在inode的哈希表中进行查找与dev设备号对应的inode,如果没找到的话,
则通过bdev伪文件系统创建bdev_inode(包含inode和block device的结构体)*/
inode = iget5_locked(blockdev_superblock, hash(dev),
bdev_test, bdev_set, &dev);
if (!inode)
return NULL;
//通过inode获取bdev_inode,再通过bdev_inode获取block device实例
bdev = &BDEV_I(inode)->bdev;
if (inode->i_state & I_NEW) {
/*分别设置block device和inode的相关域*/
bdev->bd_contains = NULL;
bdev->bd_inode = inode;
bdev->bd_block_size = (1 << inode->i_blkbits);
bdev->bd_part_count = 0;
bdev->bd_invalidated = 0;
inode->i_mode = S_IFBLK;
inode->i_rdev = dev;
inode->i_bdev = bdev;
inode->i_data.a_ops = &def_blk_aops;
mapping_set_gfp_mask(&inode->i_data, GFP_USER);
inode->i_data.backing_dev_info = &default_backing_dev_info;
spin_lock(&bdev_lock);
list_add(&bdev->bd_list, &all_bdevs);
spin_unlock(&bdev_lock);
unlock_new_inode(inode);
}
return bdev;
}