塊設備的分區信息由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;
}