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. 参考资料

相关推荐
Java爱好狂.7 小时前
Java初学者如何一周速通SpringBoot!
java·spring boot·后端·spring·高并发·架构师·java面试
马良神笔9 小时前
2026 年Rag最近技术前沿总结
后端·python·django
程序猿DD9 小时前
同样10美元,OpenDesign和Command Code的DeepSeek V4.1 Flash额度到底有多少?
后端
高频因子挖掘机10 小时前
A股量化从日线升级到5分钟线:数据量增加后最先暴露哪些工程问题?
后端·github·api
高频因子挖掘机10 小时前
历史行情返回顺序变了,为什么按行号计算指标很危险?
后端·github·api
团子哒哒11 小时前
数组、切片和Map
后端
专注仿真12 小时前
ThinkPHP6 API接口开发实例:从零搭建RESTful风格接口
后端·php·restful
开心猴爷12 小时前
应对iOS审核4.3:可以使用 IpaGuard 来给属性名混淆的技术细节与平衡之道
后端·ios
1m5555555512 小时前
RAG 工程实战(五):正确知识已经进入 Prompt,为什么大模型还是会答错?
后端·面试
易小尊12 小时前
我的模型在 65% 以上时准得可怕,在 50% 时像在掷硬币
后端