本文所有内容基于linux v3.2.40。
bdget_disk()是块设备操作中的常见函数,主要用于获取指定设备所对应的block_device,它的函数原型为:struct block_device *bdget_disk(struct gendisk *disk, int partno)。
对块设备有些了解的都知道,gendisk是底层物理设备的一个抽象,比如一块硬盘在块设备驱动模型中就对应一个gendisk。gendisk又包括多个分区,每个分区有各自的分区号,分区号从数字1开始,数字0用于表示gendisk本身。
因此,从bdget_disk()的参数列表可以看出来,该函数的实际作用为:返回第i个分区的block_device,如果i为0,返回整个设备gendisk的block_device。但其实这只是bdget_disk的一个作用,在它的内部实现中,还会有另外一个处理。
先看一下函数实现:
-
struct block_device *bdget_disk(struct gendisk *disk, int partno)
-
{
-
struct hd_struct *part;
-
struct block_device *bdev = NULL;
-
-
part = disk_get_part(disk, partno); /* 找到目标分区 */
-
if (part) {
-
/* 根据分区的设备号找到对应的block_device */
-
bdev = bdget(part_devt(part));
-
}
-
disk_put_part(part); /* 减小引用计数 */
-
-
return bdev;
-
}
可以看到bdget_disk()主要靠bdget()完成,我们再追进去:
-
struct block_device *bdget(dev_t dev)
-
{
-
struct block_device *bdev;
-
struct inode *inode;
-
-
/* 查找目标inode是否存在,如果不存在则创建并初始化
-
* !!!! 注意: 对于块设备而言,在创建inode的同时,会创建block_device
-
*/
-
inode = iget5_locked(blockdev_superblock, hash(dev),
-
bdev_test, bdev_set, &dev);
-
-
if (!inode) {
-
return NULL;
-
}
-
-
/* inode与block_device被放在同一个结构体中,所以根据inode可快速找到bdev */
-
bdev = &BDEV_I(inode)->bdev;
-
-
/* 如果inode是新申请的,进行必要的初始化 */
-
if (inode->i_state & I_NEW) {
-
bdev->bd_contains = NULL;
-
bdev->bd_super = 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对应的设备号
-
inode->i_bdev = bdev; //关联inode与struct block_device
-
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;
-
}
这里出现了一个新的结构inode,关于inode与block_device的关系,如果不是很清楚,可以参考我的另一篇博客(
点这里)。
大体浏览一下上面的代码也能看出端倪,inode并不一定是原来就存在的(也就是block_device不存在),如果不存在需要创建。
我们知道,inode根据设备号在内核中维护了一个哈希表,在这里当我们将设备号传入iget5_locked()函数中后,会扫描该哈希表,如果没有找到inode,则会使用blockdev_superblock提供的alloc_inode函数申请一个inode节点,该节点为struct bdev_inode,是block_device和inode的结合:
-
struct bdev_inode {
-
struct block_device bdev;
-
struct inode vfs_inode;
-
};
也就是说,对于块设备而言,在创建inode的时候,就同时创建了block_device。inode创建完成后,会将新的inode添加到哈希表中。
bdget()函数的第20行,会对返回的inode进行判断,如果是新创建的inode,进行一些必要的初始化。
看到这里应该明白了,bdget_disk()函数实现两个功能:
1) 创建block_device。
2) 获取block_device。
其实内核中有两处调用点分别对应以上两个功能:
1) 块设备注册调用add_disk()时,会调用该函数为新的块设备创建对应的block_device。
2) 打开块设备文件(open操作)时,会根据设备号查找目标设备对应的block_device。当然,这个时候可能需要直接调用bdget()。
以上就是关于函数bdget_disk的解析,如有错误欢迎批评指正。
阅读(9331) | 评论(2) | 转发(0) |