首页 > 系统相关 >深入理解Linux内核中的虚拟文件系统(VFS)

深入理解Linux内核中的虚拟文件系统(VFS)

时间:2024-11-11 17:50:58浏览次数:3  
标签:文件 myfs struct VFS 文件系统 内核 Linux inode

深入理解Linux内核中的虚拟文件系统(VFS)

1. 引言

今天我们要探讨的是Linux内核中的虚拟文件系统(VFS)。VFS作为一层抽象,为各种不同的文件系统提供了一个统一的接口。无论是你常用的ext4,还是远程的NFS,都能通过VFS提供的相同接口进行交互。这期教程我会带你深入了解VFS的核心原理,剖析它的重要数据结构和操作接口,让你掌握文件系统在Linux中的运作机制。

2. VFS的基本概念

2.1 文件系统的抽象层

VFS的设计初衷是为了隔离文件系统与上层应用,确保内核在访问文件时不需要考虑底层文件系统的具体实现。这样做的好处是可以用一种通用的方式去处理不同的文件系统,不仅简化了代码,也方便了后续的维护工作。

2.2 VFS的统一访问接口

为了给上层应用提供一致的体验,VFS定义了一套标准接口,包括但不限于打开文件、读写文件、列出目录等。这意味着,只要你实现了这些接口,你的文件系统就可以被Linux内核所管理,这无疑为文件系统的多样化发展提供了强大的支持。

2.3 支持多文件系统的优势

多文件系统支持是Linux的一大特色。想象一下,在同一台Linux服务器上,你可以同时挂载ext4XFSNFS文件系统。所有这些文件系统的操作都通过VFS统一的接口完成,而VFS会根据挂载的信息智能地选择正确的底层文件系统操作。这样的设计极大地提高了系统的灵活性和可扩展性。

2.4 VFS的作用示例 – 文件打开过程

文件打开是一个非常基础但也相当重要的操作,它涉及到了从系统调用到具体文件系统实现的一系列步骤。这里我简要地为大家介绍一下这个过程:

  1. 用户程序发起open()系统调用。
  2. 内核接收到请求后,调用do_sys_open()函数,开始解析路径并查找相应的目录项(dentry)。
  3. 接着,调用vfs_open()函数,尝试查找或创建一个对应的file结构。
  4. 最后,调用具体文件系统实现的file_operations.open()函数,完成文件的打开操作。

示例代码

int vfs_open(const struct path *path, struct file *file)
{
    struct inode *inode = d_backing_inode(path->dentry);
    file->f_op = fops_get(inode->i_fop);

    if (file->f_op && file->f_op->open)
        return file->f_op->open(inode, file);
    return 0;
}

上面这段代码展示了vfs_open()函数是如何工作的,它通过dentry找到了对应的inode,进而获取了文件操作集file_operations。随后,调用了具体文件系统的open()操作来完成文件的打开。

  • 当然,文件的读写过程也遵循类似的步骤。
  1. 用户态调用read()write()
  2. 内核通过vfs_read()vfs_write()调用具体文件的file_operations中的read()write()
  3. 具体文件系统实现函数执行读写操作,并返回结果。

3. 关键数据结构

接下来,我要介绍几个VFS中极为重要的数据结构。这些结构体相互协作,确保了文件系统操作的顺利进行。让我们一起来看看吧!

3.1 super_block(超级块)

super_block是每个挂载文件系统的核心数据结构,它包含了文件系统的状态信息以及文件系统级别的操作接口。每当一个文件系统被挂载时,就会在内存中创建一个super_block结构体,用来存储文件系统的元数据,并提供全局的操作和管理接口。

关键字段解析:
  • s_magic:文件系统的魔数,用于唯一识别文件系统类型。比如,ext4文件系统的魔数是0xEF53
  • s_op:指向super_operations结构体的指针,定义了超级块的操作接口,如文件系统的挂载、卸载等, 下文会详细讲解。
  • s_fs_info:指向与特定文件系统相关的私有数据结构的指针,通常用于存储文件系统的元数据或额外的操作数据。
  • s_flags:文件系统标志,用于存储文件系统的一些状态信息(如是否只读等)。
  • s_root:指向根目录项dentry的指针,提供文件系统根目录的访问。
  • s_type:指向file_system_type结构体的指针,定义了文件系统的类型及相关操作。

3.2 inode(索引节点)

inode是文件的核心数据结构,包含了文件的元数据信息,如文件的大小、权限、时间戳等。值得注意的是,inode结构体并不存储文件的名字,文件名是由dentry(目录项)来关联的。每个文件和目录都有一个唯一的inode,通过inode存储文件的基本属性。

常见字段:
  • i_mode:表示文件类型和权限的位掩码。例如,S_IFREG表示普通文件,S_IFDIR表示目录,S_IRUSR表示用户读权限等。
  • i_uidi_gid:文件的所有者和所属组的用户ID和组ID。
  • i_size:文件的大小,以字节为单位。
  • i_atimei_mtimei_ctime:文件的访问时间、修改时间和状态改变时间。
  • i_fop:指向file_operations结构的指针,定义了文件的操作接口,如读取、写入、关闭等, 下文会详细讲解。
  • i_op:指向inode_operations结构的指针,定义了与inode相关的操作,如创建、查找、删除等, 下文会详细讲解。

3.3 dentry(目录项)

dentry是文件路径中的一个节点,它连接了文件的名称与对应的inode。通过dentry,内核可以高效地查找文件路径和对应的inodedentry结构体是VFS的关键组成部分,它利用缓存机制(dentry cache)加速路径解析和文件查找过程。

常见字段:
  • d_name:文件的名称。是一个字符串,表示目录项的文件名。
  • d_parent:指向父目录项的指针。通过d_parent,可以追溯到目录项的父目录。
  • d_inode:指向与该目录项关联的inode结构体的指针。它表示该目录项对应的文件或目录的元数据。
  • d_flags:指示目录项状态的标志,例如是否已缓存等。
  • d_seq:用于序列化dentry缓存的访问,确保多线程或多进程环境下对目录项缓存的安全访问。

3.4 file(文件)

file结构体代表了一个打开的文件实例。当进程打开一个文件时,内核会创建一个file对象,用于跟踪文件的状态信息,比如当前的读写位置、文件的访问模式等。每个进程对同一文件的访问都会有一个独立的file结构。

常见字段:
  • f_op:指向file_operations结构体的指针,定义了文件的具体操作,如读写、关闭等, 下文会详细讲解。
  • f_pos:表示当前文件的偏移量。文件的每次读写操作都会更新f_pos,以指示文件当前的读写位置。
  • f_flags:文件打开时的标志,如只读、只写、追加等。
  • f_count:文件的引用计数,跟踪文件被多少个进程打开。
  • f_mapping:指向address_space结构的指针,表示文件内容在内存中的映射关系。

4. VFS的回调函数与操作接口

在Linux内核中,虚拟文件系统(VFS)层提供了抽象的文件系统接口,使得上层应用可以使用统一的方式访问不同的文件系统。VFS通过定义一系列的xxx_operations结构体为文件系统提供了一组标准的操作接口。这些接口允许文件系统实现者定义特定的回调函数来定制文件、目录、超级块等对象的行为,进而支持各种不同特性的文件系统。

4.1 file_operations

file_operations结构体定义了文件相关的操作集,是所有文件在VFS中的行为接口。它用于描述文件如何被读取、写入、打开、关闭等。当文件被打开时,VFS会创建一个file结构体实例,该结构体包含了指向file_operations中各个操作的指针,从而决定了文件的具体操作行为。

常见的回调函数及其作用
  • read:读取文件内容,通常需要从底层存储设备读取数据并将其复制到用户空间。
  • write:向文件写入数据,通常涉及将用户空间的数据写入到存储设备。
  • open:打开文件时调用,用于进行必要的初始化工作,比如设置文件偏移量。
  • release:关闭文件时调用,负责清理open时分配的资源。
  • llseek:改变文件的读/写位置。
  • ioctl:执行设备特有的I/O控制操作。
  • mmap:将文件映射到内存中,以便可以通过内存访问来读写文件。
示例代码
struct file_operations myfs_file_operations = {
    .owner = THIS_MODULE, // 模块所有者
    .read = myfs_read,
    .write = myfs_write,
    .open = myfs_open,
    .release = myfs_release,
    .llseek = generic_file_llseek, // 使用通用实现
    // 更多操作...
};

4.2 inode_operations

inode_operations结构体用于定义文件节点的操作接口,特别是那些涉及到目录管理、文件属性更改、符号链接处理等操作。每个inode对象都包含了一个指向inode_operations结构体的指针,这使得VFS能够通过调用相应的函数来操作文件系统的元数据。

常见的回调函数及其作用
  • lookup:在给定的目录中查找指定的文件或子目录。
  • create:在一个目录下创建新的文件。
  • mkdir:创建一个新的目录。
  • rmdir:删除一个空目录。
  • symlink:创建符号链接。
  • unlink:删除一个文件。
示例代码
struct inode_operations myfs_inode_operations = {
    .lookup = myfs_lookup,
    .create = myfs_create,
    .mkdir = myfs_mkdir,
    .rmdir = myfs_rmdir,
    .symlink = myfs_symlink,
    .unlink = myfs_unlink,
    // 更多操作...
};

4.3 super_operations

super_operations结构体定义了超级块的操作接口,超级块是文件系统的核心数据结构之一,它包含了文件系统的状态信息以及根目录的inode等。通过super_operations,VFS可以执行与整个文件系统相关联的操作,例如挂载、卸载、超级块的读写等。

常见的回调函数及其作用
  • alloc_inode:为新的inode分配内存。
  • destroy_inode:销毁不再使用的inode。
  • write_inode:将inode的信息写回存储设备。
  • drop_inode:当inode的引用计数降为0时调用,决定是否立即删除inode。
  • put_super:卸载文件系统时释放超级块的资源。
  • statfs:获取文件系统的统计信息。
示例代码
struct super_operations myfs_super_operations = {
    .alloc_inode = myfs_alloc_inode,
    .destroy_inode = myfs_destroy_inode,
    .write_inode = myfs_write_inode,
    .drop_inode = generic_delete_inode, // 使用通用实现
    .put_super = myfs_put_super,
    .statfs = myfs_statfs,
    // 更多操作...
};

4.4 其他常用操作接口

除了上述主要的操作接口外,VFS还提供了其他一些重要的操作接口,它们对于特定功能的支持至关重要。

  • dentry_operations:这个结构体定义了目录项(dentry)的操作,主要用于路径管理和性能优化。常见的操作包括d_revalidate(重新验证路径)、d_hash(计算散列值)等。
  • address_space_operations:用于管理文件与内存之间的映射关系,例如,当文件被映射到进程地址空间时,就需要用到这个接口提供的方法。常用的回调函数有readpage(读取单个页面)、writepage(写入单个页面)等。

这些接口共同构成了VFS的强大功能,使得Linux内核能够支持广泛的文件系统类型,并且能够高效地管理文件和目录。对于想要深入了解Linux内核文件系统机制的开发者来说,熟悉这些接口是非常有益的。


5. 实例分析:一个文件系统在VFS中的注册与使用

下面我们通过一个自定义文件系统的实例,详细介绍文件系统在VFS中的注册与使用过程。

5.1 文件系统的挂载流程

挂载一个文件系统意味着将其与VFS的结构连接起来,这样用户就可以通过VFS提供的统一接口来访问文件系统了。挂载过程大致可以分为以下几个步骤:

  1. 注册文件系统:在文件系统能够被挂载之前,它必须先向内核注册自己。这是通过register_filesystem函数完成的,该函数接收一个指向struct file_system_type的指针作为参数。这个结构体包含了文件系统的基本信息和挂载方法。
  2. 解析挂载选项:当用户尝试挂载一个文件系统时,内核会解析提供的挂载选项,这些选项可能包括文件系统的挂载点、挂载模式(只读或读写)以及其他特定于文件系统的选项。
  3. 分配和初始化super_block:接下来,内核会调用文件系统提供的get_sb方法(通常是在struct file_system_typemount成员中指定的),来获取并初始化一个超级块(super_block)。超级块是一个非常重要的数据结构,它包含了文件系统的基本信息,如文件系统的大小、状态、根目录的inode等。
  4. 挂载点关联:一旦超级块被正确初始化,内核就会将这个超级块与用户指定的挂载点关联起来。这意味着,超级块中的根目录dentry会被设置为挂载点的根目录,用户就可以通过这个挂载点访问文件系统了。

好的,我们可以在5.2节中增加您提到的代码示例,并解释myfs_get_inode函数的重要性。以下是修改后的5.2节内容:

5.2 struct file_system_type的作用和实现

struct file_system_type是VFS用来描述一个具体文件系统的结构体。它包含了文件系统的名称、挂载方法以及其他一些元数据信息。这个结构体对于文件系统的注册和管理至关重要。以下是struct file_system_type的一些关键字段:

  • name:文件系统的名称,这是一个字符串,用于在挂载时标识文件系统。
  • mount:这是一个函数指针,指向挂载文件系统时调用的函数。这个函数通常负责创建并初始化超级块。
  • kill_sb:这是一个函数指针,指向卸载文件系统时调用的清理函数。这个函数负责释放超级块所占用的所有资源。
  • owner:通常设置为THIS_MODULE,表示该文件系统是由哪个模块提供的。
示例代码
static struct file_system_type myfs_type = {
    .owner   = THIS_MODULE,
    .name    = "myfs",
    .mount   = myfs_mount,
    .kill_sb = myfs_kill_superblock,
};

static int __init myfs_init(void) {
    return register_filesystem(&myfs_type);
}

static void __exit myfs_exit(void) {
    unregister_filesystem(&myfs_type);
}

module_init(myfs_init);
module_exit(myfs_exit);
填充超级块信息

在挂载文件系统时,myfs_mount函数会调用myfs_get_sb来获取并初始化超级块。myfs_get_sb函数内部会调用myfs_fill_super来填充超级块的具体信息。以下是myfs_fill_super的实现:

// 填充超级块信息
static int myfs_fill_super(struct super_block *sb, void *data, int silent) {
    sb->s_magic = MYFS_MAGIC;  // 设置文件系统的魔数
    sb->s_op = &myfs_super_ops;  // 设置超级块操作
    sb->s_fs_info = NULL;  // 没有额外的文件系统信息

    // 创建根目录inode,并将其设置为超级块的根目录
    struct inode *root_inode = myfs_get_inode(sb, NULL, S_IFDIR | 0755, 0);
    if (!root_inode)
        return -ENOMEM;  // 内存分配失败

    sb->s_root = d_make_root(root_inode);  // 创建根目录的dentry
    if (!sb->s_root) {
        iput(root_inode);  // 如果创建失败,释放root_inode
        return -ENOMEM;
    }

    return 0;
}
myfs_get_inode函数的重要性
  • myfs_get_inode函数在文件系统挂载过程中起着至关重要的作用。它的主要职责是创建并初始化一个inode结构体,这个inode代表了文件系统中的一个文件或目录。具体来说:

    1. 创建inodemyfs_get_inode函数负责分配并初始化一个新的inode结构体。这个inode将包含文件的元数据信息,如文件类型、权限、大小等。
    2. 设置初始状态:在创建inode时,可以设置其初始状态,例如文件类型(普通文件、目录等)和权限(读、写、执行等)。
    3. 关联超级块:创建的inode将与超级块关联,确保文件系统中的所有inode都属于同一个文件系统。
  • 在上述代码中,myfs_get_inode函数被调用来创建根目录的inode。如果创建成功,再通过d_make_root函数创建根目录的dentry,并将这个dentry设置为超级块的根目录

  • 以后在这个新的文件系统下在创建文件/目录就会调用这个inode中的操作方法, 例如再创建一个子目录, 就会调用这个inode中的inode_operations->mkdir(), 在mkdir中又会调用myfs_get_inode创建一个新的inode, 如此生生不息

myfs_get_inode示例代码
// 获取或创建inode
static struct inode *myfs_get_inode(struct super_block *sb, struct dentry *dentry, umode_t mode, dev_t dev) {
    struct inode *inode = new_inode(sb);
    if (!inode)
        return NULL;

    inode->i_ino = get_next_ino();  // 分配inode编号
    inode->i_mode = mode;  // 设置文件类型和权限
    inode->i_uid = current_fsuid();  // 设置文件所有者
    inode->i_gid = current_fsgid();  // 设置文件所属组
    inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);  // 设置时间戳

    if (S_ISREG(mode)) {  // 如果是普通文件
        inode->i_fop = &myfs_file_operations;
    } else if (S_ISDIR(mode)) {  // 如果是目录
        inode->i_op = &myfs_dir_inode_operations;
        inode->i_fop = &myfs_dir_operations;
    } else if (S_ISLNK(mode)) {  // 如果是符号链接
        inode->i_op = &myfs_symlink_inode_operations;
    }

    unlock_new_inode(inode);
    return inode;
}

通过这种方式,myfs_get_inode函数确保了文件系统中的每一个文件和目录都有一个对应的inode,并且这些inode能够正确地与VFS的其他数据结构(如dentry)关联起来,从而实现文件系统的正常操作。

5.3 自定义文件系统的示例分析

为了更好地理解文件系统在VFS中的注册与使用,我们可以通过一个简化的自定义文件系统实现流程来进行说明。这个流程大致可以分为以下几个步骤:

  1. 定义文件系统类型:首先,我们需要定义一个struct file_system_type类型的变量来描述我们的文件系统。这个结构体中最重要的两个字段是mountkill_sb,分别指向挂载和卸载文件系统的函数。

  2. 实现挂载函数myfs_mount函数是挂载文件系统时调用的关键函数。在这个函数中,我们需要做的是创建并初始化一个超级块,然后设置根目录dentry。超级块的初始化可能涉及到从磁盘读取文件系统的元数据,或者如果是内存文件系统的话,则可能是直接在内存中创建这些元数据。

  3. 定义super_operationsinode_operations:为了使文件系统具有实际的功能,我们还需要定义超级块操作和inode操作。这些操作定义了文件系统的行为,例如如何创建文件、如何查找文件等。这些操作通常是在struct super_operationsstruct inode_operations结构体中定义的。

  4. 注册和使用:最后一步是将文件系统注册到内核中,这可以通过调用register_filesystem函数来完成。一旦文件系统被成功注册,我们就可以使用mount命令来挂载它了。例如,mount -t myfs /dev/sdb1 /mnt/myfs会将设备/dev/sdb1上的myfs文件系统挂载到/mnt/myfs目录下。

示例代码


static int
myfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
	    struct dentry *dentry, umode_t mode, dev_t dev)
{
	struct inode * inode = myfs_get_inode(dir->i_sb, dir, mode, dev);
	int error = -ENOSPC;

	if (inode) {
		d_instantiate(dentry, inode);
		dget(dentry);	/* Extra count - pin the dentry in core */
		error = 0;
		inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
	}
	return error;
}

static int myfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
		       struct dentry *dentry, umode_t mode)
{
	int retval = myfs_mknod(&nop_mnt_idmap, dir, dentry, mode | S_IFDIR, 0);
	if (!retval)
		inc_nlink(dir);
	return retval;
}

static int myfs_create(struct mnt_idmap *idmap, struct inode *dir,
			struct dentry *dentry, umode_t mode, bool excl)
{
	return myfs_mknod(&nop_mnt_idmap, dir, dentry, mode | S_IFREG, 0);
}

static int myfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
			 struct dentry *dentry, const char *symname)
{
	struct inode *inode;
	int error = -ENOSPC;

	inode = myfs_get_inode(dir->i_sb, dir, S_IFLNK|S_IRWXUGO, 0);
	if (inode) {
		int l = strlen(symname)+1;
		error = page_symlink(inode, symname, l);
		if (!error) {
			d_instantiate(dentry, inode);
			dget(dentry);
			inode_set_mtime_to_ts(dir,
					      inode_set_ctime_current(dir));
		} else
			iput(inode);
	}
	return error;
}

static int myfs_tmpfile(struct mnt_idmap *idmap,
			 struct inode *dir, struct file *file, umode_t mode)
{
	struct inode *inode;

	inode = myfs_get_inode(dir->i_sb, dir, mode, 0);
	if (!inode)
		return -ENOSPC;
	d_tmpfile(file, inode);
	return finish_open_simple(file, 0);
}

// 文件的 inode 操作定义
struct inode_operations myfs_file_inode_ops = {
    .setattr = simple_setattr,
    .getattr = simple_getattr,
};

// 目录的inode操作
struct inode_operations myfs_dir_inode_ops = {
	.create		= myfs_create,
	.lookup		= simple_lookup,
	.link		= simple_link,
	.unlink		= simple_unlink,
	.symlink	= myfs_symlink,
	.mkdir		= myfs_mkdir,
	.rmdir		= simple_rmdir,
	.mknod		= myfs_mknod,
	.rename		= simple_rename,
	.tmpfile	= myfs_tmpfile,
};

// 超级块操作定义
struct super_operations myfs_super_ops = {
    .statfs = simple_statfs,  // 获取文件系统的状态
    .drop_inode = generic_delete_inode,  // 使用默认的drop_inode实现
};

// 填充超级块信息
static int myfs_fill_super(struct super_block *sb, void *data, int silent) {
    sb->s_magic = MYFS_MAGIC;  // 设置文件系统的魔数
    sb->s_op = &myfs_super_ops;  // 设置超级块操作
    sb->s_fs_info = NULL;  // 没有额外的文件系统信息

    // 创建根目录inode,并将其设置为超级块的根目录
    struct inode *root_inode = myfs_get_inode(sb, NULL, S_IFDIR | 0755, 0);
    if (!root_inode)
        return -ENOMEM;  // 内存分配失败

    sb->s_root = d_make_root(root_inode);  // 创建根目录的dentry
    if (!sb->s_root) {
        iput(root_inode);  // 如果创建失败,释放root_inode
        return -ENOMEM;
    }

    return 0;
}

// 挂载文件系统
static struct dentry *myfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) {
    return mount_nodev(fs_type, flags, data, myfs_fill_super);  // 使用无设备挂载
}

// 卸载文件系统时的操作
static void myfs_kill_super(struct super_block *sb) {
    pr_info("myfs: superblock is being killed\n");
}

// 文件系统类型定义
static struct file_system_type myfs_type = {
    .name = "myfs",  // 文件系统名称
    .mount = myfs_mount,  // 挂载函数
    .kill_sb = myfs_kill_super,  // 卸载函数
    .owner = THIS_MODULE,  // 模块所有者
};

// 初始化文件系统模块
static int __init myfs_init(void) {
    int ret = register_filesystem(&myfs_type);  // 注册文件系统
    if (ret)
        printk(KERN_ERR "myfs: unable to register myfs\n");
    else
        printk(KERN_INFO "myfs: myfs registered\n");

    return ret;
}

// 清理文件系统模块
static void __exit myfs_exit(void) {
    unregister_filesystem(&myfs_type);  // 卸载文件系统
    printk(KERN_INFO "myfs: myfs unregistered\n");
}

// 模块入口和出口
module_init(myfs_init);
module_exit(myfs_exit);

以上就是一个简化版的自定义文件系统实现的例子。当然,在实际的应用中,文件系统的实现可能会更加复杂,涉及到更多的数据结构和算法。但是,上述示例提供了一个基本框架,可以帮助初学者了解文件系统在Linux内核中的注册与使用过程。

6. 总结

在这篇教程中,我们深入探讨了Linux内核中的虚拟文件系统(VFS)。VFS作为一个抽象层,为不同类型的文件系统提供了一个统一的接口,使得上层应用程序可以使用相同的API来访问文件,无论底层文件系统是什么类型。我们重点介绍了以下几个方面:

6.1 VFS的基本概念

  • 文件系统的抽象层:VFS隔离了文件系统与上层应用,确保内核在访问文件时不需要考虑底层文件系统的具体实现。
  • VFS的统一访问接口:VFS定义了一套标准接口,包括打开文件、读写文件、列出目录等,使得不同文件系统可以通过统一的接口进行操作。
  • 支持多文件系统的优势:Linux支持多种文件系统的同时挂载,通过VFS统一的接口,提高了系统的灵活性和可扩展性。
  • VFS的作用示例 – 文件打开过程:我们详细介绍了文件打开的过程,从用户程序发起open()系统调用,到内核调用具体文件系统的open()操作,每一步都进行了详细的解析。

6.2 关键数据结构

  • super_block(超级块):每个挂载文件系统的核心数据结构,包含文件系统的状态信息和操作接口。
  • inode(索引节点):文件的核心数据结构,包含文件的元数据信息。
  • dentry(目录项):文件路径中的一个节点,连接文件名称与对应的inode
  • file(文件):代表一个打开的文件实例,用于跟踪文件的状态信息。

6.3 VFS的回调函数与操作接口

  • file_operations:定义了文件相关的操作集,如读取、写入、打开、关闭等。
  • inode_operations:定义了文件节点的操作接口,如查找、创建、删除等。
  • super_operations:定义了超级块的操作接口,如挂载、卸载、超级块的读写等。
  • 其他常用操作接口:如dentry_operationsaddress_space_operations,用于路径管理和文件与内存的映射关系。

6.4 实例分析:一个文件系统在VFS中的注册与使用

  • 文件系统的挂载流程:从注册文件系统到解析挂载选项,再到分配和初始化超级块,最后将超级块与挂载点关联。
  • struct file_system_type的作用和实现:描述文件系统的结构体,包含文件系统的名称、挂载方法等。
  • 自定义文件系统的示例分析:通过一个简化的自定义文件系统实现流程,详细说明了文件系统在VFS中的注册与使用过程。

通过这篇教程,我们希望能够帮助你更深入地理解Linux内核中的虚拟文件系统(VFS),掌握其核心原理和关键技术点,为你的开发工作提供有力的支持。希望你在未来的开发中能够灵活运用这些知识,解决实际问题。如果你有任何疑问或需要进一步的帮助,请随时留言交流!

标签:文件,myfs,struct,VFS,文件系统,内核,Linux,inode
From: https://blog.csdn.net/weixin_47763623/article/details/143673264

相关文章

  • 《Linux操作系统》课程标准
      《Linux操作系统》是计算机类专业的一门专业课程,是培养和检验学生在Linux平台上熟练使用Linux操作系统,掌握基本服务器配置与管理等综合应用能力的一门重要的实践性课程。目的是掌握LINUX的安装与启动、LINUX远程登录、LINUX的磁盘文件管理,学会正则表达式、shell编程、用......
  • Linux基础(2)以及资源耗尽病毒的编写(详见B站泷羽sec)
    免责声明:本教程作者及相关参与人员对于任何直接或间接使用本教程内容而导致的任何形式的损失或损害,包括但不限于数据丢失、系统损坏、个人隐私泄露或经济损失等,不承担任何责任。所有使用本教程内容的个人或组织应自行承担全部风险。Linux目录介绍:/bin 二进制可执行文......
  • linux 计算程序运行时间, 及时间差
    linux计算程序运行时间,及时间差统计Shell脚本执行时间,帮助分析改进脚本执行linuxshell计算时间差值#!/bin/bash#计算时间差date1=$(date+"%Y-%m-%d%H:%M:%S")echo"时间1:$date1"echo"延时10s"sleep10date2=$(date+"%Y-%m-%d%H:%M:%S")echo"时间2......
  • Linux kernel 堆溢出利用方法(二)
    前言本文我们通过我们的老朋友heap_bof来讲解Linuxkernel中off-by-null的利用手法。在通过讲解另一道相对来说比较困难的kerneloff-by-null+dockerescape来深入了解这种漏洞的利用手法。(没了解过docker逃逸的朋友也可以看懂,毕竟有了root权限后,docker逃逸就变的相对简单了)。......
  • Linux常用命令之touch命令详解
    touch命令详解touch是一个在Unix和类Unix操作系统(如Linux和macOS)中广泛使用的命令行工具,主要功能包括更新文件的时间戳(访问时间和修改时间)和创建新的空文件。touch命令非常灵活,可以通过多种选项来定制其行为,以满足不同的需求。基本语法touch命令的基本语法如......
  • 内核参数pci=realloc
    内核参数pci=realloc在Linux系统中,pci=realloc是一个内核启动参数,用于控制PCI设备所需的内存基地址寄存器(BaseAddressRegisters,BARs)的重新分配。这个参数对于解决一些PCI设备在启动时由BIOS分配的内存地址不正确、不兼容或者无法满足特定需求的问题非常有用。PCI设备的BAR......
  • linux中使用cd指令跳转路径时带不带“/”
    在Linux中使用`cd`命令跳转目录时,是否需要`/`取决于路径的类型。以下是规则:1.**绝对路径:以`/`开头**-如果路径以`/`开头,表示从根目录开始的**绝对路径**。-使用绝对路径可以精确定位到文件系统中的某个目录,不受当前目录影响。-例如:```bashcd/......
  • linux进程概念
    前言:进程是linux中非常重要的概念,执行的每一个程序都是进程。因此我们需要了解进程。1.冯洛伊曼体系结构我们常见的计算机以及不常见的计算机大多都遵循冯洛伊曼体系结构。冯洛伊曼体系结构由五部分组成,分别是输出设备,输入设备,存储器,运算器和控制器组成。输入设备包括键......
  • apt-get——Debian Linux发行版中的APT软件包管理工具
    转自于:https://github.com/jaywcjlove/linux-command,https://blog.csdn.net/liudsl/article/details/79200134后不赘述apt-getDebianLinux发行版中的APT软件包管理工具,现在更推荐使用apt,相关命令附后。说明apt-get命令是DebianLinux发行版中的APT软件包管理工具。所有......
  • Linux中文件系统层次结构简述
    在Linux操作系统中,并没有像Windows那样的“盘符”概念。相反,Linux使用一个统一的文件系统层次结构,所有的文件和目录都挂载在一个单一的根目录/下。这种设计使得文件系统的管理更加灵活和一致。文件系统层次结构在Linux中,文件系统通常按照以下层次结构组织:/(根目录):文件系......