Linux 内核源码分析---处理 VFS 对象及标准函数

news2024/9/20 18:32:37

处理VFS对象

注册文件系统:在文件系统注册到内核时,文件系统是编译为模块,或者持久编译到内核中。
在这里插入图片描述
fs/super.c 中的register_filesystem用来向内核注册文件系统。我们可以通过/proc/filesystems查看系统所有的文件系统类型。
一个文件系统不能注册两次,否则,将描述新文件系统的对象置于链表末尾,这样就完成向内核的注册。

static struct file_system_type ext4_fs_type = {
	.owner		= THIS_MODULE,
	.name		= "ext4",
	.mount		= ext4_mount,
	.kill_sb	= kill_block_super,
	.fs_flags	= FS_REQUIRES_DEV,
};

int register_filesystem(struct file_system_type * fs)
{
	int res = 0;
	struct file_system_type ** p;

	BUG_ON(strchr(fs->name, '.'));
	if (fs->next)
		return -EBUSY;
	write_lock(&file_systems_lock);
	//在已注册的全局文件系统链表file_systems中查找,看是否已经注册过
	p = find_filesystem(fs->name, strlen(fs->name));
	if (*p)
		//如果查找到就返回错误,同一个文件系统不能注册两次
		res = -EBUSY;
	else
		//没查找到,直接将该文件系统添加链表末尾
		*p = fs;
	write_unlock(&file_systems_lock);
	return res;
}

static struct file_system_type **find_filesystem(const char *name, unsigned len)
{
	struct file_system_type **p;
	//在已注册的全局文件系统链表file_systems中查找
	for (p = &file_systems; *p; p = &(*p)->next)
		if (strncmp((*p)->name, name, len) == 0 &&
		    !(*p)->name[len])
			break;
	return p;
}

在这里插入图片描述

装载和卸载:目录树的装载和卸载比仅仅注册文件系统复杂得多,因为后者只需要向一个链表添加对象,而前者需要对内核的内部数据结构执行很多操作,所以要复杂得多。文件系统的装载由 mount 系统调用发起。我们需要阐明在现存目录树中装载新的文件系统必须执行的任务。还需要用于描述装载点的数据结构。

在这里插入图片描述

  • vfsmount 结构:采用一种单一的文件系统层次结构,新的文件系统可以集成到其中,使用 mount 可查询目录树中各种文件系统的装载情况如下:
    在这里插入图片描述
    将文件系统装载到一个目录时,装载点的内容被替换为即将装载的文件系统的相对根目录的内容,前一个目录数据消失,直到新文件系统卸载才重新出现。
    vfsmount 结构描述一个独立文件系统的挂载信息,每个不同挂载点对应一个独立的 vfsmount 结构,属于同一文件系统的所有目录和文件隶属于同一个 vfsmount,该 vfsmount 结构对应于该文件系统顶层目录,即挂载目录。
/*fs/mount.h*/
struct mount {
	`struct` hlist_node mnt_hash;
	struct mount *mnt_parent; 				//装载点所在的父文件系统
	struct dentry *mnt_mountpoint;			//装载点在父文件系统中的dentry(目录项)
	struct vfsmount mnt;
	union {
		struct rcu_head mnt_rcu;
		struct llist_node mnt_llist;
	};
#ifdef CONFIG_SMP
	struct mnt_pcp __percpu *mnt_pcp;
#else
	int mnt_count;
	int mnt_writers;
#endif
	//子文件系统链表
	struct list_head mnt_mounts;	/* list of children, anchored here */
	//链表元素,用于父文件系统中的mnt_mount链表
	struct list_head mnt_child;	/* and going through their mnt_child */
	struct list_head mnt_instance;	/* mount instance on sb->s_mounts */
	//设备名称,例如/dev/dsk/hda1
	const char *mnt_devname;	/* Name of device e.g. /dev/dsk/hda1 */
	struct list_head mnt_list;
	//链表元素,用于特定于文件系统的到期链表中
	struct list_head mnt_expire;	/* link in fs-specific expiry list */
	//链表元素,用于共享装载的循环链表
	struct list_head mnt_share;	/* circular list of shared mounts */
	//从属装载的链表
	struct list_head mnt_slave_list;/* list of slave mounts */
	//链表元素,用于从属装载的链表
	struct list_head mnt_slave;	/* slave list entry */
	//指向主装载,从属装载位于master->mnt_slave_list链表上面
	struct mount *mnt_master;	/* slave is on master->mnt_slave_list */
	//所属的命名空间
	struct mnt_namespace *mnt_ns;	/* containing namespace */
	struct mountpoint *mnt_mp;	/* where is it mounted */
	struct hlist_node mnt_mp_list;	/* list mounts with the same mountpoint */
	struct list_head mnt_umounting; /* list entry for umount propagation */
#ifdef CONFIG_FSNOTIFY
	struct hlist_head mnt_fsnotify_marks;
	__u32 mnt_fsnotify_mask;
#endif
	int mnt_id;			/* mount identifier */
	int mnt_group_id;		/* peer group identifier */
	int mnt_expiry_mark;		/* true if marked for expiry */
	struct hlist_head mnt_pins;
	struct fs_pin mnt_umount;
	struct dentry *mnt_ex_mountpoint;
};

文件系统之间的父子关系由上述两个成员实现链表表示,mnt_mounts表头是子文件系统链表的起点,而mnt_child字段则用作该链表的链表元素。
系统当中的每个vfsmount实例,通过两种途径标识一个命名空间的所有装载的文件系统都保存在namespace->list链表中。使用vfsmountmnt_list成员作为链表元素。

  • 超级块管理:在装载新的文件系统时, vfsmount 并不是唯一需要在内存中创建结构。装载操作开始于超级块的读取。
/*fs.h*/
struct super_block {
	struct list_head	s_list;	//通过该变量链接到超级块全局链表super_blocks上
	dev_t			s_dev;		//该文件系统对应的块设备标识符
	unsigned char		s_blocksize_bits;
	unsigned long		s_blocksize; //该文件系统的block size
	loff_t			s_maxbytes;	//文件系统支持的最大文件
	struct file_system_type	*s_type; //文件系统类型,比如ext3、ext4
	const struct super_operations	*s_op; //超级块的操作函数
	const struct dquot_operations	*dq_op; //文件系统限额相关操作
	const struct quotactl_ops	*s_qcop; //磁盘限额
	const struct export_operations *s_export_op;
	unsigned long		s_flags; //文件系统的mount标记
	unsigned long		s_iflags;	/* internal SB_I_* flags */
	unsigned long		s_magic;  //该文件系统类型的魔术字
	struct dentry		*s_root; //全局根目录的dentry项
	...
	struct block_device	*s_bdev;  //对应的块设备
	struct backing_dev_info *s_bdi; //超级块对应的BDI设备
	struct mtd_info		*s_mtd;
	//通过该变量,链接到file_system_type中的fs_supers链表
	struct hlist_node	s_instances;
	...
	char			s_id[32];	/* Informational name */
	uuid_t			s_uuid;		/* UUID tune2fs -l可以查看*/

	void 			*s_fs_info;	//指向具体文件系统超级块结构,如ext4_sb_info
	...
	const struct dentry_operations *s_d_op; //该超级块默认的目录项操作函数
	...
	struct shrinker s_shrink;	//每个超级块注册的shrink函数,用于内存回收
	...
	/* AIO completions deferred from interrupt context */
	struct workqueue_struct *s_dio_done_wq;
	...
	//该超级块对应的未在使用dentry列表
	struct list_lru		s_dentry_lru ____cacheline_aligned_in_smp;
	//该超级块对应的未在使用inode列表
	struct list_lru		s_inode_lru ____cacheline_aligned_in_smp;
	...
	/* s_inode_list_lock protects s_inodes */
	spinlock_t		s_inode_list_lock ____cacheline_aligned_in_smp;
	struct list_head	s_inodes;	//该超级块包含的所有inode

	spinlock_t		s_inode_wblist_lock;
	struct list_head	s_inodes_wb;	//该超级块正在回写的inode
};

s_op 指向一个包含了函数指针的结构,该结构按熟悉的VFS方式,提供了一个一般性的接口,用于处理超级块相关操作。操作的实现必须由底层文件系统的代码提供。该结构定义如下:

struct super_operations {
   	struct inode *(*alloc_inode)(struct super_block *sb);
   	//将inode从内存和底层的存储介质删除
	void (*destroy_inode)(struct inode *);
	//将传递的inode结构标记为“脏的”,意思就是修改过
   	void (*dirty_inode) (struct inode *, int flags);
	int (*write_inode) (struct inode *, struct writeback_control *wbc);
	int (*drop_inode) (struct inode *);
	void (*evict_inode) (struct inode *);
	void (*put_super) (struct super_block *);
	int (*sync_fs)(struct super_block *sb, int wait);
	int (*freeze_super) (struct super_block *);
	int (*freeze_fs) (struct super_block *);
	int (*thaw_super) (struct super_block *);
	int (*unfreeze_fs) (struct super_block *);
	int (*statfs) (struct dentry *, struct kstatfs *);
	int (*remount_fs) (struct super_block *, int *, char *);
	int (*remount_fs2) (struct vfsmount *, struct super_block *, int *, char *);
	void *(*clone_mnt_data) (void *);
	void (*copy_mnt_data) (void *, void *);
	void (*umount_begin) (struct super_block *);

	int (*show_options)(struct seq_file *, struct dentry *);
	int (*show_options2)(struct vfsmount *,struct seq_file *, struct dentry *);
	int (*show_devname)(struct seq_file *, struct dentry *);
	int (*show_path)(struct seq_file *, struct dentry *);
	int (*show_stats)(struct seq_file *, struct dentry *);
#ifdef CONFIG_QUOTA
	ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
	ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
	struct dquot **(*get_dquots)(struct inode *);
#endif
	int (*bdev_try_to_free_page)(struct super_block*, struct page*, gfp_t);
	long (*nr_cached_objects)(struct super_block *,
				  struct shrink_control *);
	long (*free_cached_objects)(struct super_block *,
				    struct shrink_control *);
};
  • mount 系统调用:
    mount 系统调用的入口点是 sys_mount 函数,由sys_mount从用户空间复制到内核空间之后,内核将控制转移给do_mount
    在这里插入图片描述
    在这里插入图片描述

  • 共享子树
    共享子树最核心的特征是允许挂载和卸载事件以一种自动的,可控的方式在不同的 namespaces间传递(propagation)。这就意味着,在一个命名空间中挂载光盘的同时也会触发对于其他namespace对同一张光盘的挂载。
    在共享子树中,每个挂载点都存在一个名为传递类型(propagation type)的标记,该标记决定了一个namespace中创建或者删除的挂载点是否会传递到其他的namespaces
    共享子树有四种传递类型:
    MS_SHARED:该挂载点和它的共享挂载和卸载事件。
    MS_PRIVATE:和共享挂载相反,标记为private的事件不会传递到任何的对等组,挂载操作默认使用次标志。
    MS_SLAVE:这个传递类型介于sharedslave之间,一个slave mount拥有一个master(一个共享的对等组),slave mount不能将事件传递给master mount
    MS_UNBINDABLE:该挂载点是不可缩写的。

标准函数

VFS层提供的有用资源是用于读写数据的标准函数。这些操作对所有文件系统来说,在一定程度上都是相同的。
如果数据所在的块是已知的,则首先查询页缓存。如果数据并未保存在其中,则向对应的块设备发出读请求。
如果对每个文件系统都需要实现这些操作,则会导致代码大量复制,我们应该不惜代价防止这种情况发生。

常用 VFS与 read/write 系统调用,如vfs_readvfs_write

在这里插入图片描述
在这里插入图片描述

VFS(虚拟文件系统,Virtual File System) 是物理文件系统与服务之间的接口层,向下对文件系统提供标准接口,方便其他文件系统移植,向上对应用层提供标准文件操作接口,使open()、read()、write()等系统调用可以跨越各种文件系统和不同介质执行。

在这里插入图片描述

超级块对象 super block:对应已装载的文件系统,用来描述整个文件系统的信息,每个具体的文件系统都有自己的超级块,所有超级块对象以双向链循环链表的形成连接,超级块对象在文件系统装载时创建,保存在内存中,在文件系统超载时它会自动删除。
索引节点对象 inode,对应介质上的一个文件,索引节点对象包含内核在操作文件或目录时需要的全部信息。
目录项对象 dentry:对应一个目录项,目录项对象没有对应的磁盘数据结构(三种状态:被使用、未使用、负状态)。
文件对象file:对应由进程所打开的文件。所有定义在linux/fs.h.文件对象表示进程已打开的文件。由open()系统调用创建,由close()系统调用删除,多个进程同时打开和操作同一对象,存在多个对应的文件对象。

系统调用 _sys_read 会调用到 vfs 层的_vfs_read接口,在 vfs 层接口会调用具体文件系统操作由内核来完成。
从内核文件系统看文件读写过程
sys_write()系统调用:
在这里插入图片描述

open/read 等系统调用

在这里插入图片描述

#include <unistd.h>
size_t write(int flides, const void *buf, size_t nbytes);

write 系统调用,是把缓存区buf中的前nbytes字节写入到与文件描述符flides有关的文件中,返回的是实际写入到文件中的字节数。

#include <unistd.h>
size_t read(int flides, void *buf, size_t nbytes);

read 系统调用,是从与文件描述符flides相关联的文件中读取前nbytes字节的内容,并且写入到数据区buf中,返回的是实际读入的字节数

open 有两种调用方法

int open(const *path, int oflags);

将准备打开的文件或是设备的名字作为参数path传给函数,oflags用来指定文件访问模式,成功返回一个新的文件描述符,失败返回 -1
必需部分:
O_RDONLY:以只读方式打开
O_WRONLY:以只写方式打开
O_RDWR:以读写方式打开
可选部分:
O_CREAT:按照参数mode给出的访问模式创建文件
O_EXCL:与O_CREAT一起使用,确保创建出文件,避免两个程序同时创建同一个文件,如文件存在则open调用失败;
O_APPEND:把写入数据追加在文件的末尾;
O_TRUNC:把文件长度设置为0,丢弃原有内容;

int open(const *path, int oflags, mode_t mode);

在第一种调用方式上,加上了第三个参数 mode,主要是搭配O_CREAT使用,这个参数规定了属主、同组和其他人对文件的文件操作权限。
在这里插入图片描述

int close(int flides);

close系统调用,终止文件描述符 flides 与其对应的文件间的联系,文件描述符被释放,可重新使用。

https://www.cnblogs.com/jimbo17/p/10107318.html
https://blog.csdn.net/qq_39755395/article/details/78516383

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1983682.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Idea绿色下载安装教程-最新,2024版本通用-附下载链接

插件链接&#xff1a; 脚本 Idea下载安装完成后 进入激活码输入页面&#xff0c;然后关闭IDEA 按照下面流程进行激活 1. 按照以下步骤&#xff0c;亲测可用&#xff0c;记得一定要先关闭idea 2. 选择对应软件 3.选择bin、目录对应选项 5.激活 6.成功

MySQL--读写分离与分布式存储

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 一、读写分离 1、什么是读写分离 在数据库集群架构中&#xff0c;让主库负责处理写入操作&#xff0c;而从库只负责处理select查询&#xff0c;让两…

部署MySQL数据库时常见错误

登录数据库 时&#xff0c;可能会出现如图错误 1.确认是否部署MySQL服务 2.过滤MySQL端口号&#xff0c;查看服务是否开启&#xff08;如图上半部分&#xff0c;则是服务未开启&#xff09; 3.如图部分&#xff0c;则是密码错误 4.如果忘记了 mysql 的密码&#xff0c;或者登陆…

Apple Vision Pro 游戏开发:挑战与反思

随着Apple Vision Pro的推出,许多游戏开发者开始尝试在这个全新的平台上构建沉浸式的虚拟现实体验。然而,开发者们很快发现,在这个新兴领域中面临着不少挑战,包括支付延迟、技术支持不足、设备性能限制等问题。本文将探讨这些挑战,并提出一些开发者需要注意的关键点。 支…

Qt:自定义钟表组件

使用QWidget绘制两种钟表组件&#xff0c;效果如下&#xff1a; 源码下载链接&#xff1a;GitHub - DengYong1988/Clock-Widget: Qt 自定义钟表组件 https://download.csdn.net/download/ouyangxiaozi/89616407 主要代码如下&#xff1a; ClockWgt.h #ifndef CLOCKWGT_H #d…

spring源码阅读-推断构造方法

如何构造一个对象&#xff1f; 1、默认情况下&#xff0c;或者只有一个构造方法的情况下&#xff0c;就会使用默认构造方法或者唯一的一个构造方法 2、由程序员指定构造方法入参值&#xff0c;通过getBean()的方式获取&#xff0c;可以指定参数类型以及个数&#xff0c;但是该…

【康复学习--LeetCode每日一题】572. 另一棵树的子树

题目&#xff1a; 给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree …

SpringBoot3里的文件上传

需求分析&#xff1a; 在用户更换头像或者添加文章时&#xff0c;都需要携带一个图片的URL访问地址。当用户访问文件上传接口将图片的数据上传成功后&#xff0c;服务器会返回一个地址。我们后台需要提供一个文件上传的接口&#xff0c;用来接收前端提交的文件的数据并且返回文…

C++入门基础(完整版)含下卷

C入门基础 hello 各位未来的程序员大佬们&#xff0c;这一篇是详细介绍入门基础&#xff0c;和上一篇不同的是这一篇补完了引用的知识和inline&#xff0c;nullptr的知识&#xff0c;希望大家有所收获 namespace的价值 在C/C中&#xff0c;变量、函数和后⾯要学到的类都是⼤…

【w门大学】云计算与大数据零基础特训班视频教程合辑

提取地址 云计算与大数据零基础特训班 课程目录

“数据要素×”大赛江西分赛官网正式上线 共设置12个赛道

7月17日&#xff0c;2024年“数据要素”大赛江西分赛在南昌市拉开帷幕。作为全国“数据要素”大赛的地方分赛&#xff0c;江西分赛由国家数据局、江西省人民政府指导&#xff0c;江西省发展改革委&#xff08;省数据局&#xff09;联合18家省级单位共同主办&#xff0c;江西分赛…

CCRC-DSA数据安全评估师:ISC.AI2024数字安全峰会:安全大模型引领安全行业革命

7月31日&#xff0c;以“构建先进的安全模型&#xff0c;引领安全产业变革”为主题&#xff0c;ISC.AI 2024数字安全峰会在北京国家会议中心成功举办。 本次峰会旨在鼓励行业通过大规模模型重构安全框架&#xff0c;确保数字经济的稳健前进。 会上&#xff0c;众多院士级专家…

【pytorch】全连接网络简单二次函数拟合

下面是一个使用PyTorch实现全连接网络来拟合简单二次函数 y x 2 y x^2 yx2 的示例。我们将创建一个简单的神经网络&#xff0c;定义损失函数和优化器&#xff0c;并进行训练。 下面是完整的代码示例&#xff1a; import torch import torch.nn as nn import torch.optim …

Linux笔记(1)

在敲出 文件 &#xff0f; 目录 &#xff0f; 命令 的前几个字母之后&#xff0c;按下 tab 键 如果输入的没有歧义&#xff0c;系统会自动补全 如果还存在其他 文件 &#xff0f; 目录 &#xff0f; 命令 &#xff0c;再按一下 tab 键&#xff0c;系统会提示可能存在的命令 小…

【算法速刷(5/100)】LeetCode —— 20.有效的括号

题目要求比较明晰简洁&#xff0c;编码难度并不算高 下面贴出代码和思路 bool isValid(string s) {stack<char> stk;for(const char& c : s){if(stk.empty()){stk.push(c);continue;}if(c ( || c [ || c {){stk.push(c);continue;}else{char top stk.top();boo…

Java Lambda表达式总结(快速上手图解)

Java Lambda表达式总结&#xff08;快速上手详解&#xff09;-CSDN博客https://blog.csdn.net/m0_66070037/article/details/140912566?spm1001.2014.3001.5501

idea 设置自动移除多余 的import (或整个项目删除)

1、开发的时候&#xff0c;直接自动去除多余的引入 2、如果想把整个项目的移除可以如下操作

Linux 异常 sh: /usr/bin/xauth: not found

异常原因&#xff1a; 远程连接时出现错误&#xff0c;由于 Omega 上没有 X 环境&#xff0c;因此在尝试交换凭据以设置转发时会失败 解决办法&#xff1a; 进入高级 SHH 设置&#xff0c;并勾选了 X11 转发。取消勾选该框。

Studying-代码随想录训练营day59| dijkstra(堆优化版)精讲、Bellman_ford 算法精讲

第59天&#xff0c;dijkstra算法的优化版本&#xff0c;以及Bellman_ford 算法&#x1f4aa;&#x1f4aa;(ง •_•)ง&#xff0c;编程语言&#xff1a;C 目录 dijkstra&#xff08;堆优化版&#xff09;精讲 思路 图的存储 邻接矩阵 邻接表 本题图的存储 堆优化细节…

x86 8086 CPU 寄存器详解

8086 寄存器及其地址详细介绍 8086 微处理器是 Intel 在 1978 年推出的一款 16 位微处理器&#xff0c;它在计算机体系结构的发展中占有重要地位。8086 处理器拥有 14 个 16 位寄存器&#xff0c;这些寄存器被分为几类&#xff1a;通用寄存器、段寄存器、指令指针寄存器和标志…