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

相关推荐
bluebonnet274 分钟前
【Rust练习】22.HashMap
开发语言·后端·rust
uhakadotcom27 分钟前
如何实现一个基于CLI终端的AI 聊天机器人?
后端
Iced_Sheep1 小时前
干掉 if else 之策略模式
后端·设计模式
XINGTECODE1 小时前
海盗王集成网关和商城服务端功能golang版
开发语言·后端·golang
程序猿进阶1 小时前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
FIN技术铺2 小时前
Spring Boot框架Starter组件整理
java·spring boot·后端
凡人的AI工具箱2 小时前
15分钟学 Go 第 60 天 :综合项目展示 - 构建微服务电商平台(完整示例25000字)
开发语言·后端·微服务·架构·golang
先天牛马圣体2 小时前
如何提升大型AI模型的智能水平
后端
java亮小白19972 小时前
Spring循环依赖如何解决的?
java·后端·spring
2301_811274312 小时前
大数据基于Spring Boot的化妆品推荐系统的设计与实现
大数据·spring boot·后端