Linux 虚拟文件系统(二)--挂载文件系统

1. 前言

在上一篇文章中,我介绍了 VFS 中的重量级嘉宾 inode 结构体。inode 结构体是 VFS 的实现关键,但是俗语有云:"一个篱笆三个桩,一个好汉三个帮"。没有其他相关结构的帮助,仅凭 inode 结构体是没法实现 VFS 这么庞大的系统。

VFS 涉及的内核对象比较多,如果一股脑地搬出来,恐怕会杂乱无章,让人看了头大。一个文件系统要想注册到 VFS 进行使用,一般需要经历注册文件系统到内核、过载文件系统然后就可以对文件系统进行读写操作了。那么我们不妨按照这个顺序,将每个过程涉及到的数据结构一一呈现出来。如此一来,更具逻辑性。

2. 注册文件系统

文件系统实现两种注册进内核的方式,持久化编译到内核,内核的文件系统启动时便注册;另一种时将文件系统实现编译为模块,在相关内核载入内核时进行注册。

这两种注册方法本质上没有区别,都是使用 register_filesystem 函数向内核注册文件系统实现。只是注册时机不同而已。调用 register_filesystem 时,会将文件系统封装成一个file_system_type结构体,并使用结构体中的next字段将多个文件系统链接成一个链表。

下面是file_system_type结构体定义,在调用register_filesystem函数向内核注册文件系统时,内核会判断是否存在相同的name。如果相同则阻止注册。

arduino 复制代码
struct file_system_type { 
       const char *name; // 文件系统名称
       int fs_flags; 
       struct super_block *(*get_sb) (struct file_system_type *, int, 
                                         const char *, void *, struct vfsmount *); 
       // 获取超级块
       void (*kill_sb) (struct super_block *); 
       struct module *owner; 
       struct file_system_type * next; 
       struct list_head fs_supers; 
}; 

3. 挂载文件系统

类 UNIX 系统大多采用了单一文件系统的层次结构,新的文件系统可以集成到其中。用通俗点的话说就是只有一颗文件树,新的文件系统可以找这颗文件树任意一个叶子进行挂载。这里就产生了一个新的概念,挂载点。

如上图所示,是一颗文件树。其中相同颜色的是同一个文件系统。不难看出,图中所示共有三个文件系统。而其中/mnt和/mnt/usb则被称为挂载点,这是因为这些位置是装载文件系统的地方。

每个装载的文件系统都会产生一个vfsmount实例。vfsmount中包含指向文件系统的超级块和挂载点的目录。每挂载一个文件系统都在生成对应的vfsmount结构。

arduino 复制代码
struct vfsmount {
	struct dentry *mnt_root;	/* root of the mounted tree */
	struct super_block *mnt_sb;	/* pointer to superblock */
	int mnt_flags;
	struct mnt_idmap *mnt_idmap;
};

vfsmount并非挂载文件系统时需要在内存创建的唯一结构。超级块(superblock)是文件系统挂载的重要结构。文件系统的挂载开始于超级块的读取。

超级块是一个非常关键的数据结构,它存储了有关文件系统的整体信息和状态的元数据。超级块通常位于文件系统的开始部分,并且包含了文件系统的全局参数。

arduino 复制代码
struct super_block {
	struct list_head	s_list;		/* Keep this first */
	dev_t			s_dev;		/* search index; _not_ kdev_t */
	unsigned char		s_blocksize_bits; // 块大小--比特
	unsigned long		s_blocksize; // 块大小
	loff_t			s_maxbytes;	/* 最大文件大小 */
	struct file_system_type	*s_type; // 关联的文件系统 
	const struct super_operations	*s_op; // 超级块支持的操作
	unsigned long		s_flags;
	unsigned long		s_iflags;	/* internal SB_I_* flags */
	unsigned long		s_magic;
	struct dentry		*s_root;  // 挂载的目录
	struct rw_semaphore	s_umount;
	int			s_count; // 引用计数
	atomic_t		s_active;
	struct hlist_bl_head	s_roots;	/* alternate root dentries for NFS */
	struct list_head	s_mounts;	/* list of mounts; _not_ for fs use */
	struct block_device	*s_bdev;
	struct hlist_node	s_instances;

	struct sb_writers	s_writers;

	void			*s_fs_info;	/* Filesystem private info */

	/* Granularity of c/m/atime in ns (cannot be worse than a second) */
	u32			s_time_gran;
	/* Time limits for c/m/atime in seconds */
	time64_t		   s_time_min;
	time64_t		   s_time_max;

	char			s_id[32];	/* Informational name */
	uuid_t			s_uuid;		/* UUID */

	unsigned int		s_max_links;

	const struct dentry_operations *s_d_op; /* default d_op for dentries */

	struct shrinker *s_shrink;	/* per-sb shrinker handle */

	/* Number of inodes with nlink == 0 but still referenced */
	atomic_long_t s_remove_count;

	/*
	 * Number of inode/mount/sb objects that are being watched, note that
	 * inodes objects are currently double-accounted.
	 */
	atomic_long_t s_fsnotify_connectors;

	/* Read-only state of the superblock is being changed */
	int s_readonly_remount;

	/* per-sb errseq_t for reporting writeback errors via syncfs */
	errseq_t s_wb_err;

	/* AIO completions deferred from interrupt context */
	struct workqueue_struct *s_dio_done_wq;
	struct hlist_head s_pins;

	/*
	 * The list_lru structure is essentially just a pointer to a table
	 * of per-node lru lists, each of which has its own spinlock.
	 * There is no need to put them into separate cachelines.
	 */
	struct list_lru		s_dentry_lru;
	struct list_lru		s_inode_lru;
	struct rcu_head		rcu;
	struct work_struct	destroy_work;

	struct list_head	s_inodes;	/* 文件系统所有的inode */

	spinlock_t		s_inode_wblist_lock;
	struct list_head	s_inodes_wb;	/* 等待写回的inode列表 */
};

超级块除了保存一些文件系统的基本信息之外,还维护了文件系统下所有关联 inode 结构。同时维护所有待写回的inode结构。

4. 参考资料

相关推荐
余槐i2 小时前
PostgreSQL 函数索引报错 must be immutable:3种绕法与代价
数据库·后端·sql·postgresql·性能优化
需要8262 小时前
Spring Bean 生命周期:从实例化到销毁的完整钩子
java·后端·spring
凤山老林3 小时前
Spring Boot 接入国密 SM2/SM3/SM4 的一些实践记录
spring boot·后端·python
伯恩bourne4 小时前
Spring Boot Bean 注册的两种方式:@Component 家族 vs @Configuration + @Bean
java·spring boot·后端
张彦峰ZYF5 小时前
停顿短不等于延迟低:G1、ZGC 与低延迟服务的目标分野、代价账本与选型标定
后端·回收暂停、应用停止总时长·到达安全点、分配停滞·屏障吃吞吐、并发线程吃核心·并发窗口吃堆余量·分区式靠挑回收集兑现目标·染色指针式不支持压缩引用
pride.li5 小时前
第03章 Python 教程-基础语法:注释与变量
服务器·前端·后端·python·算法·计算机视觉
upala6 小时前
【仓颉语言入门 · 第二篇 第 6 课】
后端
罗丹丹Luna6 小时前
移动端显示“已提交”以后:SAP 与低代码集成该怎样暴露真实状态
后端·架构
码事漫谈6 小时前
因为太想讨好你,所以AI会骗你
后端
吴凡wudianmei6 小时前
每调一次模型都花钱?如何用工程手段把调用压到最少?
前端·后端·架构