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

相关推荐
2401_882727574 小时前
低代码配置式组态软件-BY组态
前端·后端·物联网·低代码·前端框架
追逐时光者5 小时前
.NET 在 Visual Studio 中的高效编程技巧集
后端·.net·visual studio
大梦百万秋6 小时前
Spring Boot实战:构建一个简单的RESTful API
spring boot·后端·restful
斌斌_____6 小时前
Spring Boot 配置文件的加载顺序
java·spring boot·后端
路在脚下@6 小时前
Spring如何处理循环依赖
java·后端·spring
海绵波波1077 小时前
flask后端开发(1):第一个Flask项目
后端·python·flask
小奏技术8 小时前
RocketMQ结合源码告诉你消息量大为啥不需要手动压缩消息
后端·消息队列
AI人H哥会Java10 小时前
【Spring】控制反转(IoC)与依赖注入(DI)—IoC容器在系统中的位置
java·开发语言·spring boot·后端·spring
凡人的AI工具箱10 小时前
每天40分玩转Django:Django表单集
开发语言·数据库·后端·python·缓存·django
奔跑草-10 小时前
【数据库】SQL应该如何针对数据倾斜问题进行优化
数据库·后端·sql·ubuntu