每个块设备用一个块设备结构进行描述,其结构如下:
struct block_device {
dev_t bd_dev; /* not a kdev_t - it's a search key */
struct inode * bd_inode; //多大用处
int bd_openers;//打开该设备计数值
struct semaphore bd_sem; //打开或关闭互斥
struct semaphore bd_mount_sem; /* mount mutex */
struct list_head bd_inodes;//设备节点链表
void * bd_holder;
int bd_holders;
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;
struct backing_dev_info *bd_inode_backing_dev_info;//反向设备信息
unsigned long bd_private;//私有数据
};
块设备的管理是以一个块设备伪文件系统组织的,每个块设备是这个文件系统上的一个块设备节点,其节点结构如下:
struct bdev_inode {
struct block_device bdev;//块设备结构
struct inode vfs_inode;//在vfs中的节点
};
static struct vfsmount *bd_mnt;//块设备文件系统在VFS中的挂载点结构体
struct super_block *blockdev_superblock;//块设备文件系统超级块结构体,由于操作块设备文件系统
void __init bdev_cache_init(void)
{
int err;
bdev_cachep = kmem_cache_create("bdev_cache", sizeof(struct bdev_inode),
0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|SLAB_PANIC,
init_once, NULL);
//为struct bdev_inode建立slab缓存,用于分配对象给具体的块设备,用于分配节点以及
//其他缓存
err = register_filesystem(&bd_type);//注册块设备文件系统
if (err)
panic("Cannot register bdev pseudo-fs");
bd_mnt = kern_mount(&bd_type);//将块设备文件系统挂载到vfs文件系统链表
err = PTR_ERR(bd_mnt);
if (IS_ERR(bd_mnt))
panic("Cannot create bdev pseudo-fs");
blockdev_superblock = bd_mnt->mnt_sb;//得到块设备文件系统的超级块
}
其为块文件系统的结构类型结构体:
static struct file_system_type bd_type = {
.name = "bdev",//名称
.get_sb = bd_get_sb,//得到超级块函数
.kill_sb = kill_anon_super,
};
static struct super_block *bd_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
//分配超级块结构 根节点 根目录的入口结构并初始化
return get_sb_pseudo(fs_type, "bdev:", &bdev_sops, 0x62646576);
}
其结构示意图如下:
超级块的操作函数集如下:
static struct super_operations bdev_sops = {
.statfs = simple_statfs,
.alloc_inode = bdev_alloc_inode,//分配节点
.destroy_inode = bdev_destroy_inode,//销毁节点
.drop_inode = generic_delete_inode,//通用删除节点
.clear_inode = bdev_clear_inode,//将设备节点从总设备节点上删除
};
以bdev_alloc_inode为例:
static struct inode *bdev_alloc_inode(struct super_block *sb)
{
struct bdev_inode *ei = kmem_cache_alloc(bdev_cachep, SLAB_KERNEL);
//为某个块设备分配一个块设备节点
if (!ei)
return NULL;
return &ei->vfs_inode;//还回节点结构
}
static void bdev_destroy_inode(struct inode *inode)
{
struct bdev_inode *bdi = BDEV_I(inode);//根据节点获取块节点地址
bdi->bdev.bd_inode_backing_dev_info = NULL;
kmem_cache_free(bdev_cachep, bdi);//释放slab对象,即节点缓存
}
块设备伪文件的文件操作函数集如下:
struct file_operations def_blk_fops = {
.open = blkdev_open,//打开设备函数
.release = blkdev_close,//关闭设备函数
.llseek = block_llseek,//移动设备函数
.read = generic_file_read,//块设备读函数
.write = blkdev_file_write,//块设备写函数
.aio_read = generic_file_aio_read,//地址空间IO读函数
.aio_write = blkdev_file_aio_write,//地址空间IO写函数
.mmap = generic_file_mmap,
.fsync = block_fsync,
.unlocked_ioctl = block_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = compat_blkdev_ioctl,
#endif
.readv = generic_file_readv,
.writev = generic_file_write_nolock,
.sendfile = generic_file_sendfile,
};
当打开设备文件时,文件系统sys_open->filp_open->dentry_open->blkdev_open的任务就根据主设备号找到相应的数据结构。
只分析打开函数:
static int blkdev_open(struct inode * inode, struct file * filp)
{
struct block_device *bdev;
int res;
filp->f_flags |= O_LARGEFILE;//容许大文件获取
bdev = bd_acquire(inode);//获取块设备
res = do_open(bdev, filp);
if (res)
return res;
if (!(filp->f_flags & O_EXCL) )
return 0;
if (!(res = bd_claim(bdev, filp)))
return 0;
blkdev_put(bdev);
return res;
}
其中bd_acquire用于获取块设备结构体,若节点中块设备存在则提取即可;若块设备不存在则需要根据设备号
在块设备文件系统中查找响应的节点并且建立新的块设备。由bdget函数完成。
static struct block_device *bd_acquire(struct inode *inode)
{
struct block_device *bdev;
spin_lock(&bdev_lock);
bdev = inode->i_bdev;//获取块设备结构
if (bdev && igrab(bdev->bd_inode)) {//如果块设备存在且节点不为空
spin_unlock(&bdev_lock);
return bdev;//返回块设备结构体地址
}
//为空时或节点为空时
spin_unlock(&bdev_lock);
bdev = bdget(inode->i_rdev);//根据设备号建立块设备结构和对应的节点
if (bdev) {
spin_lock(&bdev_lock);
if (inode->i_bdev)
__bd_forget(inode);//将设备删除
inode->i_bdev = bdev;
//将新建的块设备赋给节点,此时的节点与块设备中的节点不同故而赋值一次
inode->i_mapping = bdev->bd_inode->i_mapping;
//实质将新建节点的i_mapping给老节点i_mapping
list_add(&inode->i_devices, &bdev->bd_inodes);//添加到块设备节点链表
spin_unlock(&bdev_lock);
}
return bdev;
}
bdget如下所示:
struct block_device *bdget(dev_t dev)//根据设备号建立块设备结构
{
struct block_device *bdev;
struct inode *inode;
inode = iget5_locked(bd_mnt->mnt_sb, hash(dev),
bdev_test, bdev_set, &dev);//从挂载的块文件体统中获取新的节点
if (!inode)
return NULL;
bdev = &BDEV_I(inode)->bdev;//节点中设备与给新结构体
if (inode->i_state & I_NEW) {//若节点是新的则初始化
bdev->bd_contains = NULL;//分区为空
bdev->bd_inode = inode;//将新建的块文件系统中的节点保存到块设备中便于以后使用
bdev->bd_block_size = (1 << inode->i_blkbits);
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;
}将查找新的节点且新建块设备结构体并初始化。