bdev文件系统
在上一章中讲解了Linux块设备驱动架构以及块设备驱动的一些入门的部分,也提及这一章中主要讲解bdev文件系统。
bdev文件系统是一个内核文件系统也叫做伪文件系统,在应用层或者叫做用户层是不可见的。这样对于读者来说理解起来比较吃力的,bdev文件系统实际上就是按照文件系统的组织方式来呈现给内核的,没有什么特殊的含义,如果说从功能上来说的话对于读者比较好理解:bdev文件系统主要管理着块设备中的struct block_device结构,而块设备结构又被包含在struct blkdev_inode结构中,所以bdev文件系统直接管理着blkdev_inode结构间接管理着块设备结构。
如果按照定义来说bdev文件系统管理着块设备结构的申请和消除。
bdev文件系统的数据结构:
如果说从需求方面来理解bdev文件系统的数据结构包括实现是比较好理解的。我们从文章开始提到的bdev文件系统的介绍入手,抽出来两个关键点供大家参考:
1.bdev既然是一个文件系统肯定有与文件系统管理有关的数据结构:blkdev_superblock全局变量。
2.bdev文件系统既然直接管理管理struct blkdev_inode,肯定有与blkdev_inode管理有关的数据结构。
从以上方面入手了解会是比较简单的,下面我们分析一下bdev文件系统两个主要的数据结构:
1.struct super_block *blockdev_superblock __read_mostly;这个结构与VFS 文件系统相关;
2.static struct kmem_cache * bdev_cachep __read_mostly;与内存申请有关的缓存池;
3.struct file_system_type bdev_type;这个结构与vfs文件系统有关;
这两个主要的数据结构代表的意义在以上的解释中有解释,有些不再一一说明;但是重点说明一下bdev_type操作和bdev_type有段的VFS文件系统操作:
static struct file_system_type bd_type = {
.name = "bdev",
.get_sb = bd_get_sb, #获取超级块;
.kill_sb = kill_anon_super,
};
static const 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,
};
static int bd_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data, struct vfsmount *mnt)
{
return get_sb_pseudo(fs_type, "bdev:", &bdev_sops, 0x62646576, mnt);
}
/*
*/
bd_get_sb是注册进入文件系统时候所要调用的,其主要作用是吧bdev_sops放入blkdev_superblock中,用于文件系统中inode(也即是struct blkdev_inode)的申请和释放。
bdev文件系统的执行过程
1.注册
bdev文件系统在开机的时候注册的,具体执行代码流程如下:
start_kernel
|
V
vfs_caches_init
|
V
bdev_cache_init
|
V
先申请缓存池bdev_cachep
再注册文件系统bdev_type;
具体的执行过程大家一一跟踪代码就OK,下面重点介绍的是bdev_cache_init:
void __init bdev_cache_init(void)
{
int err;
struct vfsmount *bd_mnt;
bdev_cachep = kmem_cache_create("bdev_cache", sizeof(struct bdev_inode),
0, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
SLAB_MEM_SPREAD|SLAB_PANIC),
init_once); //缓存池申请
err = register_filesystem(&bd_type); #注册进入文件系统
if (err)
panic("Cannot register bdev pseudo-fs");
bd_mnt = kern_mount(&bd_type); # 执行kernel_mount操作;
if (IS_ERR(bd_mnt))
panic("Cannot create bdev pseudo-fs");
/*
* This vfsmount structure is only used to obtain the
* blockdev_superblock, so tell kmemleak not to report it.
*/
kmemleak_not_leak(bd_mnt);
blockdev_superblock = bd_mnt->mnt_sb; /* For writeback */
}
可以看到bdev_cachep申请过程,以及文件系统的注册过程,文件系统的注册说成也就是生成blkdev_superblock过程与blkdev_superblock和bdev_sops的关联过程。
2.使用
经过上述步骤,我们肯定需要使用bdev文件系统,如果不使用文件系统那么我们创建这个文件系统就没有作用,很多事情一样如果我们做了文章开头而没有中心思想那么这篇文章就是废的。bdev文件系统怎么使用呢?我们还是从bdev文件系统的作用开始找:bdev文件系统主要作用是管理内核blkdev_inode结构,那么这就是我们bdev文件系统的最主要的使用场景。
先不讨论如何使用的问题,我们先讨论一下bdev文件系统在哪里使用的问题,这也是我们的第二点疑惑:bdev文件系统在哪里使用的问题。
在上一章《Linux块设备总结(二)》[link][https://blog.csdn.net/weixin_37867857/article/details/88322091]中我们讨论过这个问题,在blkdev_open时候也就是设备文件打开的时候,要申请block_device结构也就是blkdev_inode结构,这也就是我们bdev文件系统的全部的应用场景。具体应用场景流程如下:
blkdev_open
|
V
bdev_acquire
|
V
bdev_get
|
V
iget5_locked 中会根据blkdev_superblock判断此blkdev是否已经存在blkdev_inode结构,如果没有则重新申请,如果有返回已经申请的blkdev_inode结构。
注意:
对于所有的块设备文件系统而言,只要主设备号和次设备号一旦确认,设备文件所对应的bdev文件系统就只有一个,无论打开多少次。
接下来讨论bdev文件系统在哪里使用的问题,这个问题比较复杂,但是由于上面文章的铺垫,我们可以总结一下使用bdev文件系统的时机问题:
1. 在块设备文件生成后可能会使用bdev文件系统;
2. 在块设备文件系统打开过程肯定会使用bdev文件系统。
为什么要说在块设备文件生成后可能会使用bdev文件系统呢?这个过程注意不要把重点放在“可能”二字上面,需要把重点放在“块设备文件生成后”。在这个过程中间会把def_blkdev_fops和块设备的inode相关联,这样我们在打开块设备文件过程中会使用bdev文件系统申请一个blkdev_inode会生成一个bdev文件系统。
bdev文件系统如何使用:
在块设备文件打开过程中会使用bdev文件系统申请blkdev_inode,这就是申请了一个bdev文件系统。一旦申请了blkdev_inode blkdev_inode直到文件系统完全关闭就不会消除这个节点。
blkdev_inode怎么释放呢?是调用文件系统中的一个通用函数clear_inode时候释放的,具体如下:
生成bdev文件系统时候:
init_vfs_inode
|
V
bdev_cache_init
|
V
blkdev_superblock中的bdev_sops中注册进入一个clear_inode方法
注销文件系统的时候
clear_inode
|
V
会根据传入的inode->i_sb->sops->clear_inode方法注销bdev文件系统中的blkdev_inode
以上这些会在VFS文件系统涉及,如果不懂得话也不要紧。
以下是bdev文件系统的主要申请步骤的代码表述:
static struct inode *bdev_alloc_inode(struct super_block *sb)
{
struct bdev_inode *ei = kmem_cache_alloc(bdev_cachep, GFP_KERNEL);
if (!ei)
return NULL;
return &ei->vfs_inode;
}
static void bdev_clear_inode(struct inode *inode)
{
struct block_device *bdev = &BDEV_I(inode)->bdev;
struct list_head *p;
spin_lock(&bdev_lock);
while ( (p = bdev->bd_inodes.next) != &bdev->bd_inodes ) {
__bd_forget(list_entry(p, struct inode, i_devices));
}
list_del_init(&bdev->bd_list);
spin_unlock(&bdev_lock);
}
通过以上代码我们可以看到bdev文件系统的一部分。并非全貌。但是有个大概的了解就比什么都重要了,这样我们可以继续介绍我们的块设备文件。
总结:
在文章开头我们从bdev文件系统的作用手,了解bdev文件系统是用于申请内核中的bdev_inode,从而知道了内核块设备文件结构体struct blockdevice的来源,这样有助于我们更加清晰明确的了解块设备文件。进而我们分析了bdev文件系统的注册过程,看到了bdev文件系统的主要申请时机是在生成块设备文件后第一次打开块设备文件过程中申请的bdev文件系统。在末尾我们略微分享了一下bdev文件系统中申请bdev_inode的过程,这样更加有助于我们理解bdev文件系统的工作原理。
<bdev文件系统完>