- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
PS:要转载请注明出处,本人版权所有。 PS: 这个只是基于《我自己》的理解, 如果和你的原则及想法相冲突,请谅解,勿喷.
无 。
VFS(Virtual File System)是一种软件抽象,主要还是为了连接用户态、内核态和实际文件系统本身。例如:我们可以write一个字符串到磁盘ext4 fs上的某个文件.
在linux里面,有各种各样的文件系统,它们实际存放的位置可能是硬盘(ext4fs等)、RAM(sysfs, procfs, devtmpfs等)、网络(nfs等)等等存储介质.
这里主要介绍VFS的基础知识,以及我们常见的文件操作怎么对应到VFS里面,主要以ext4 fs与vfs的关联为例子. 。
在vfs里面提供了一种缓存机制,缓存struct dentry项,这个dcache在内核里面表示了一个文件系统的整个文件目录信息(从根目录开始的一个目录信息),但是作为一种cache数据结构,一般会存在cache miss然后创建对应struct dentry。这样就可以很快的查找到我们传入的一个路径的文件对于的struct dentry项.
vfs通过我们常见的open接口操作文件时,一般是用路径来标识一个文件名的,那么怎么将我们传入的名字转换成对应的目录/文件信息呢?答案就是上面提到的dcache数据结构,通过查询dcache得到目录/文件信息,这个部分的内容也是open系统调用常常做的事情.
下面是dcache项的基本定义:
struct dentry {
/* RCU lookup touched fields */
unsigned int d_flags; /* protected by d_lock */
seqcount_spinlock_t d_seq; /* per dentry seqlock */
struct hlist_bl_node d_hash; /* lookup hash list */
struct dentry *d_parent; /* parent directory */
struct qstr d_name;
struct inode *d_inode; /* Where the name belongs to - NULL is
* negative */
unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */
... ...
struct super_block *d_sb; /* The root of the dentry tree */
... ...
};
还记得我们在《Linux 文件系统(一) --- ext4文件系统简介》( https://www.cnblogs.com/Iflyinsky/p/18162137 )中提到,目录也是一种文件嘛?文件又是用inode来表示的,那么struct dentry就可以得到对应的struct inode信息了.
大家应该都听过一句话,在unix里面,一切皆文件。那么对于vfs来说,一个独立的文件就是struct inode对象. 。
/*
* Keep mostly read-only and often accessed (especially for
* the RCU path lookup and 'stat' data) fields at the beginning
* of the 'struct inode'
*/
struct inode {
... ...
const struct inode_operations *i_op;
struct super_block *i_sb;
struct address_space *i_mapping;
... ...
}
其实这个struct inode和struct ext4_inode有许多相似的属性,他们也有些许关联.
对于struct inode来说,很重要的就是struct inode_operations,这个代表着我们可以对这个inode进行的操作,其结构大概如下:
struct inode_operations {
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
... ...
int (*create) (struct mnt_idmap *, struct inode *,struct dentry *,
umode_t, bool);
int (*link) (struct dentry *,struct inode *,struct dentry *);
int (*unlink) (struct inode *,struct dentry *);
... ...
}
根据父目录的对应的lookup函数,我们可以查找对应的文件inode信息。此外,还有创建删除inode节点的方法,这些方法在文件系统装载的时候实现.
我们上面讲的struct dentry和struct inode是和对应的文件系统存储的数据是息息相关的。但是实际我们操作文件的第一步是打开文件,对于一个打开的文件,在内核里面使用struct file来标识,其结构如下:
struct path {
struct vfsmount *mnt;
struct dentry *dentry;
};
/*
* f_{lock,count,pos_lock} members can be highly contended and share
* the same cacheline. f_{lock,mode} are very frequently used together
* and so share the same cacheline as well. The read-mostly
* f_{path,inode,op} are kept on a separate cacheline.
*/
struct file {
... ...
struct path f_path;
struct inode *f_inode; /* cached value */
const struct file_operations *f_op;
... ...
}
对于一个file对象来说,除了上面提到的struct dentry和struct inode关联外,还有一个重要的结构是:struct file_operations,对于这个结构来说,大家应该非常的熟悉,包含了open/close/read/write等等接口:
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
... ...
int (*open) (struct inode *, struct file *);
... ...
到这里,其实我们vfs我们常见用到的基本就讲完了,串起来是说就是open一个文件,创建一个struct file对象,关联一个struct dentry和struct inode,这时就可以对文件进行操作了. 。
我们前面提到了,vfs最终会对接到实际文件系统本身,那么VFS支持哪些FS呢?在linux的/proc/filesystems文件中,存放了当前注册到vfs的所有支持的FS.
下面我们介绍文件系统的注册和取消注册:
/*
* Filesystem context for holding the parameters used in the creation or
* reconfiguration of a superblock.
*
* Superblock creation fills in ->root whereas reconfiguration begins with this
* already set.
*
* See Documentation/filesystems/mount_api.rst
*/
struct fs_context {
const struct fs_context_operations *ops;
struct mutex uapi_mutex; /* Userspace access mutex */
struct file_system_type *fs_type;
void *fs_private; /* The filesystem's context */
void *sget_key;
struct dentry *root; /* The root and superblock */
... ...
}
struct file_system_type {
const char *name; //文件系统名字,例如ext4
int fs_flags;
int (*init_fs_context)(struct fs_context *);
const struct fs_parameter_spec *parameters;
struct dentry *(*mount) (struct file_system_type *, int,
const char *, void *);
void (*kill_sb) (struct super_block *);
struct module *owner;
struct file_system_type * next;
struct hlist_head fs_supers;
... ...
};
#include <linux/fs.h>
extern int register_filesystem(struct file_system_type *);
extern int unregister_filesystem(struct file_system_type *);
在register_filesystem/unregister_filesystem函数里面,主要是对内核里面的file_system_type变量进行链表操作,注册就是增加链表节点,取消注册就是删除链表节点.
这里我们先举个例子,对于linux来说,内核启动后第一个挂载的文件系统,也就是挂载在/根目录的文件系统。如果大家观察过一些linux的启动相关信息,有个常见的问题就是:
Error: root fs cannot be detected。
这个问题就是内核没有找到适合的根文件系统来加载,一般来说失败后会自动进入一个叫做initramfs的文件系统,方便进行诊断.
对于用户挂载和卸载文件系统来说,一般我们是使用mount/umount命令,其和内核启动后挂载第一个文件系统的操作类似,其实我们执行mount命令的时候,对调用对应的file_system_type的mount函数(看上文中的file_system_type有一个mount函数)来完成挂载的操作.
首先我们来看看mount命令是怎么到file_system_type中的mount函数的。我们来看看调用序列:
上面的操作完毕, 我们得到一个struct vfsmount 和 struct mount 对象,这个对象代表了一个文件系统的挂载基本信息。struct mount 中的mnt_instance指向的是ext4 fs 的root dentry中的super_block存放的链表。意思就是,创建好的一个文件系统,其挂载点信息可以在root dentry中找到.
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;
} __randomize_layout;
struct mount {
struct hlist_node mnt_hash;
struct mount *mnt_parent;
struct dentry *mnt_mountpoint;
struct vfsmount mnt;
... ...
struct list_head mnt_instance; /* mount instance on sb->s_mounts */
... ...
}
下面我们来讲讲ext4的file_system_type定义中,ext4_mount的调用.
注意,file_system_type的mount接口在新的内核版本中被废弃了,因为有新的mount api实现,所以在5.17-rc1中,ext4_mount这个函数无了。下面是linux kernel中这个提交的commit内容:
ext4: switch to the new mount api
Add the necessary functions for the fs_context_operations. Convert and
rename ext4_remount() and ext4_fill_super() to ext4_get_tree() and
ext4_reconfigure() respectively and switch the ext4 to use the new api.
One user facing change is the fact that we no longer have access to the
entire string of mount options provided by mount(2) since the mount api
does not store it anywhere. As a result we can't print the options to
the log as we did in the past after the successful mount.
Signed-off-by: Lukas Czerner <lczerner@redhat.com>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
Link: https://lore.kernel.org/r/20211027141857.33657-13-lczerner@redhat.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
commit-id:cebe85d570cf84804e848332d6721bc9e5300e07
下面,我们仍用ext4_mount的函数内容来讲解。对于挂载来说,一般会做如下操作(对于磁盘来说):
从上面的说明我们可以知道,一个struct super_block对象,代表一个挂载的文件系统。其定义如下:
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; /* Max file size */
struct file_system_type *s_type;
const struct super_operations *s_op;
... ...
}
从上面的结构来看,最重要的就是s_op了,这个代表对一个文件系统的一些基本操作方法。此外,对于s_list来说,很明显的表达了struct super_block会被存储到一个链表里面,在linux里面,是存放在 static LIST_HEAD(super_blocks) 变量中的.
struct super_operations {
struct inode *(*alloc_inode)(struct super_block *sb);
void (*destroy_inode)(struct inode *);
void (*free_inode)(struct 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 *);
... ...
}
上面我们提到了struct super_operations、struct inode_operations、struct file_operations这三个重要的操作,对于挂载的ext4fs来说,其实现在ext4中实现,并对应赋值给对应的指针。他们定义分别如下:
const struct file_operations ext4_file_operations = {
.llseek = ext4_llseek,
.read_iter = ext4_file_read_iter,
.write_iter = ext4_file_write_iter,
.iopoll = iocb_bio_iopoll,
.unlocked_ioctl = ext4_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ext4_compat_ioctl,
#endif
.mmap = ext4_file_mmap,
.mmap_supported_flags = MAP_SYNC,
.open = ext4_file_open,
.release = ext4_release_file,
.fsync = ext4_sync_file,
.get_unmapped_area = thp_get_unmapped_area,
.splice_read = ext4_file_splice_read,
.splice_write = iter_file_splice_write,
.fallocate = ext4_fallocate,
};
const struct inode_operations ext4_file_inode_operations = {
.setattr = ext4_setattr,
.getattr = ext4_file_getattr,
.listxattr = ext4_listxattr,
.get_inode_acl = ext4_get_acl,
.set_acl = ext4_set_acl,
.fiemap = ext4_fiemap,
.fileattr_get = ext4_fileattr_get,
.fileattr_set = ext4_fileattr_set,
};
static const struct super_operations ext4_sops = {
.alloc_inode = ext4_alloc_inode,
.free_inode = ext4_free_in_core_inode,
.destroy_inode = ext4_destroy_inode,
.write_inode = ext4_write_inode,
.dirty_inode = ext4_dirty_inode,
.drop_inode = ext4_drop_inode,
.evict_inode = ext4_evict_inode,
.put_super = ext4_put_super,
.sync_fs = ext4_sync_fs,
.freeze_fs = ext4_freeze,
.unfreeze_fs = ext4_unfreeze,
.statfs = ext4_statfs,
.show_options = ext4_show_options,
.shutdown = ext4_shutdown,
#ifdef CONFIG_QUOTA
.quota_read = ext4_quota_read,
.quota_write = ext4_quota_write,
.get_dquots = ext4_get_dquots,
#endif
};
从上面来,还没有挂载的时候,对于一个ext4fs的各种操作就已经实现了,挂载只是将这些操作实现对应赋值而已.
这里多说一句,其他的fs也会有对应operations的实现。例如:我们常见的驱动开发的时候,file_operations的填充可以说是基操.
总的来说,vfs提供了对各种fs的操作的封装。mount命令可以将特定文件系统绑定到vfs。当我们mount一个fs时,可以得到这个fs的root dentry,super_block,mount等结构信息.
我们根据一个fs的root dentry信息,可以解析出其目录下的所有文件目录结构,从而达到访问特定文件系统、特定设备的文件的目的.
PS: 请尊重原创,不喜勿喷。 PS: 要转载请注明出处,本人版权所有。 PS: 有问题请留言,看到后我会第一时间回复.
最后此篇关于Linux文件系统(二)---vfs简单分析的文章就讲到这里了,如果你想了解更多关于Linux文件系统(二)---vfs简单分析的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我刚刚继承了一个旧的 PostgreSQL 安装,需要进行一些诊断以找出该数据库运行缓慢的原因。在 MS SQL 上,您可以使用 Profiler 等工具来查看正在运行的查询,然后查看它们的执行计划。
将目标从Analytics(分析)导入到AdWords中,然后在Analytics(分析)中更改目标条件时,是否可以通过更改将目标“重新导入”到AdWords,还是可以自动选择? 最佳答案 更改目标值
我正在使用google analytics api来获取数据。我正在获取数据,但我想验证两个参数,它们在特定日期范围内始终为0。我正在获取['ga:transactions']和['ga:goalCo
我使用Google API从Google Analytics(分析)获取数据,但指标与Google Analytics(分析)的网络界面不同。 即:我在2015年3月1日获得数据-它返回综合浏览量79
我在我的Web应用程序中使用sammy.js进行剔除。我正在尝试向其中添加Google Analytics(分析)。我很快找到了following plugin来实现页面跟踪。 我按照步骤操作,页面如
当使用 Xcode 分析 (product>analyze) 时,有没有办法忽略给定文件中的任何错误? 例如编译指示之类的? 我们只想忽略第三方代码的任何警告,这样当我们的代码出现问题时,它对我们
目录 EFK 1. 日志系统 2. 部署ElasticSearch 2.1 创建handless服务 2.2 创建s
关闭。这个问题不满足Stack Overflow guidelines .它目前不接受答案。 想改善这个问题吗?更新问题,使其成为 on-topic对于堆栈溢出。 7年前关闭。 Improve thi
GCC/G++ 是否有可用于输出分析的选项? 能够比较以前的代码与新代码之间的差异(大小、类/结构的大小)将很有用。然后可以将它们与之前的输出进行比较以进行比较,这对于许多目的都是有用的。 如果没有此
我正在浏览 LYAH,并一直在研究处理列表时列表理解与映射/过滤器的使用。我已经分析了以下两个函数,并包含了教授的输出。如果我正确地阅读了教授的内容,我会说 FiltB 的运行速度比 FiltA 慢很
在 MySQL 中可以使用 SET profiling = 1; 设置分析 查询 SHOW PROFILES; 显示每个查询所用的时间。我想知道这个时间是只包括服务器的执行时间还是还包括将结果发送到前
我用 Python 编写了几个用于生成阶乘的模块,我想测试运行时间。我找到了一个分析示例 here我使用该模板来分析我的模块: import profile #fact def main():
前几天读了下mysqld_safe脚本,个人感觉还是收获蛮大的,其中细致的交代了MySQL数据库的启动流程,包括查找MySQL相关目录,解析配置文件以及最后如何调用mysqld程序来启动实例等,有着
1 内网基础 内网/局域网(Local Area Network,LAN),是指在某一区域内有多台计算机互联而成的计算机组,组网范围通常在数千米以内。在局域网中,可以实现文件管理、应用软件共享、打印机
1 内网基础 内网/局域网(Local Area Network,LAN),是指在某一区域内有多台计算机互联而成的计算机组,组网范围通常在数千米以内。在局域网中,可以实现文件管理、应用软件共享、打印机
我有四列形式的数据。前三列代表时间,value1,value 2。第四列是二进制,全为 0 或 1。当第四列中对应的二进制值为0时,有没有办法告诉excel删除时间、值1和值2?我知道这在 C++ 或
我正在运行一个进行长时间计算的 Haskell 程序。经过一些分析和跟踪后,我注意到以下内容: $ /usr/bin/time -v ./hl test.hl 9000045000050000 Com
我有一个缓慢的 asp.net 程序正在运行。我想分析生产服务器以查看发生了什么,但我不想显着降低生产服务器的速度。 一般而言,配置生产盒或仅本地开发盒是标准做法吗?另外,您建议使用哪些程序来实现这一
我目前正在尝试分析 Haskell 服务器。服务器永远运行,所以我只想要一个固定时间的分析报告。我尝试只运行该程序 3 分钟,然后礼貌地要求它终止,但不知何故,haskell 分析器不遵守术语信号,并
是否有工具可以分析 Maven 构建过程本身,以便我可以看到构建花费最多时间的地方? 我们在工作中遇到了关于 Maven 3.0.3 和 3.0b1 的问题。与 3.0.3 (9m00s) 相比,我们
我是一名优秀的程序员,十分优秀!