细说|Linux虚拟文件系统原理(细说.文件系统.原理.虚拟.Linux...........)

wufei123 2025-01-05 阅读:6 评论:0
在unix的世界里,有一句经典的话:一切对象皆是文件。这句话的含义是,可以将unix操作系统中的所有对象都视为文件,并使用文件操作接口来处理它们。linux作为一个类unix操作系统,也致力于实现这一理念。 虚拟文件系统概述 为了实现“一切...

在unix的世界里,有一句经典的话:一切对象皆是文件。这句话的含义是,可以将unix操作系统中的所有对象都视为文件,并使用文件操作接口来处理它们。linux作为一个类unix操作系统,也致力于实现这一理念。

虚拟文件系统概述

为了实现“一切对象皆是文件”的理念,Linux内核引入了一个中间层:虚拟文件系统(Virtual File System)。

如果你熟悉面向对象编程语言(如C++/Java等),你可能不陌生于“接口”这个概念。虚拟文件系统类似于面向对象中的接口,定义了一套标准的接口规范。开发者只需实现这套接口,就能够通过文件操作接口来操作对象。如下图所示:

细说|Linux虚拟文件系统原理

上图中的蓝色部分就是虚拟文件系统所在位置。

从上图可以看出,虚拟文件系统为上层应用提供了统一的接口。如果某个文件系统实现了虚拟文件系统的接口,那么上层应用就能够使用诸如 open()、read() 和 write() 等函数来操作它们。

今天,我们就来介绍虚拟文件系统的原理与实现。

虚拟文件系统原理

在阐述虚拟文件系统的原理前,我们先来介绍一个 Java 例子。通过这个 Java 例子,我们能够更容易理解虚拟文件系统的原理。

一个Java例子

如果大家使用过 Java 编写程序的话,那么就很容易理解虚拟文件系统了。我们使用 Java 的接口来模拟虚拟文件系统的定义:

public interface VFSFile {
  int open(String file, int mode);
  int read(int fd, byte[] buffer, int size);
  int write(int fd, byte[] buffer, int size);
  ...
}

上面定义了一个名为 VFSFile 的接口,接口中定义了一些方法,如 open()、read() 和 write() 等。现在我们来定义一个名为 Ext3File 的对象来实现这个接口:

public class Ext3File implements VFSFile {
  @Override
  public int open(String file, int mode) {
    ...
  }
  
  @Override
  public int read(int fd, byte[] buffer, int size) {
    ...
  }
  
  @Override
  public int write(int fd, byte[] buffer, int size) {
    ...
  }
  
  ...
}

现在我们就能使用 VFSFile 接口来操作 Ext3File 对象了,如下代码:

public class Main() {
  public static void main(String[] args) {
    VFSFile file = new Ext3File();
    
    int fd = file.open("/tmp/file.txt", 0);
    ...
  }
}

从上面的例子可以看出,底层对象只需要实现 VFSFile 接口,就可以使用 VFSFile 接口相关的方法来操作对象,用户完全不需要了解底层对象的实现过程。

虚拟文件系统原理

上面的 Java 例子已经大概说明虚拟文件系统的原理,但由于 Linux 是使用 C 语言来编写的,而 C 语言并没有接口这个概念。所以,Linux 内核使用了一些技巧来模拟接口这个概念。

下面来介绍一下 Linux 内核是如何实现的。

1. file结构

为了模拟接口,Linux 内核定义了一个名为 file 的结构体,其定义如下:

struct file {
    ...
    const struct file_operations *f_op;
    ...
};

在 file 结构中,最为重要的一个字段就是 f_op,其类型为 file_operations 结构。而 file_operations 结构是由一组函数指针组成,其定义如下:

struct file_operations {
    ...
    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 *);
    ...
};

从 file_operations 结构的定义可以隐约看到接口的影子,所以可以猜想出,如果实现了 file_operations 结构中的方法,应该就能接入到虚拟文件系统中。

在 Linux 内核中,file 结构代表着一个被打开的文件。所以,只需要将 file 结构的 f_op 字段设置成不同文件系统实现好的方法集,那么就能够使用不同文件系统的功能。

这个过程在 __dentry_open() 函数中实现,如下所示:

static struct file *
__dentry_open(struct dentry *dentry, 
              struct vfsmount *mnt, 
              truct file *f, 
              int (*open)(struct inode *, struct file *), 
              const struct cred *cred)
{
    ...
    inode = dentry->d_inode;
    ...
    // 设置file结构的f_op字段为底层文件系统实现的方法集
    f->f_op = fops_get(inode->i_fop);
    ...
    return f;
}

设置好 file 结构的 f_op 字段后,虚拟文件系统就能够使用通用的接口来操作此文件了。调用过程如下:

细说|Linux虚拟文件系统原理

2. file_operations结构

底层文件系统需要实现虚拟文件系统的接口,才能被虚拟文件系统使用。也就是说,底层文件系统需要实现 file_operations 结构中的方法集。

一般底层文件系统会在其内部定义好 file_operations 结构,并且填充好其方法集中的函数指针。如 minix文件系统 就定义了一个名为 minix_file_operations 的 file_operations 结构。其定义如下:

// 文件:fs/minix/file.c

const struct file_operations minix_file_operations = {
    .llseek         = generic_file_llseek,
    .read           = do_sync_read,
    .aio_read       = generic_file_aio_read,
    .write          = do_sync_write,
    .aio_write      = generic_file_aio_write,
    .mmap           = generic_file_mmap,
    .fsync          = generic_file_fsync,
    .splice_read    = generic_file_splice_read,
};

也就是说,如果当前使用的是 minix 文件系统,当使用 read() 函数读取其文件的内容时,那么最终将会调用 do_sync_read() 函数来读取文件的内容。

3. dentry结构

到这里,虚拟文件系统的原理基本分析完毕,但还有两个非常重要的结构要介绍一下的:dentry 和 inode。

dentry 结构表示一个打开的目录项,当我们打开文件 /usr/local/lib/libc.so 文件时,内核会为文件路径中的每个目录创建一个 dentry 结构。如下图所示:

细说|Linux虚拟文件系统原理

可以看到,file 结构有个指向 dentry 结构的指针,如下所示:

struct file {
    ...
    struct path f_path;
    ...
    const struct file_operations *f_op;
    ...
};

struct path {
    ...
    struct dentry *dentry;
};

与文件类似,目录也有相关的操作接口,所以在 dentry 结构中也有操作方法集,如下所示:

struct dentry {
    ...
    struct dentry *d_parent;              // 父目录指针
    struct qstr d_name;                   // 目录名字
    struct inode *d_inode;                // 指向inode结构
    ...
    const struct dentry_operations *d_op; // 操作方法集
    ...
};

其中的 d_op 字段就是目录的操作方法集。

内核在打开文件时,会为路径中的每个目录创建一个 dentry 结构,并且使用 d_parent 字段来指向其父目录项,这样就能通过 d_parent 字段来追索到根目录。

4. inode结构

在 Linux 内核中,inode 结构表示一个真实的文件。为什么有了 dentry 结构还需要 inode 结构呢?这是因为 Linux 存在硬链接的概念。

例如使用以下命令为 /usr/local/lib/libc.so 文件创建一个硬链接:

ln /usr/local/lib/libc.so /tmp/libc.so

现在 /usr/local/lib/libc.so 和 /tmp/libc.so 指向同一个文件,但它们的路径是不一样的。所以,就需要引入 inode 结构了。如下图所示:

细说|Linux虚拟文件系统原理

由于 /usr/local/lib/libc.so 和 /tmp/libc.so 指向同一个文件,所以它们都使用同一个 inode 对象。

inode 结构保存了文件的所有属性值,如文件的创建时间、文件所属用户和文件的大小等。其定义如下所示:

struct inode {
    ...
    uid_t           i_uid;               // 文件所属用户
    gid_t           i_gid;               // 文件所属组
    ...
    struct timespec i_atime;             // 最后访问时间
    struct timespec i_mtime;             // 最后修改时间
    struct timespec i_ctime;             // 文件创建时间
    ...
    unsigned short  i_bytes;             // 文件大小
    ...
    const struct file_operations *i_fop; // 文件操作方法集(用于设置file结构)
    ...
};

我们注意到 inode 结构有个类型为 file_operations 结构的字段 i_fop,这个字段保存了文件的操作方法集。当用户调用 open() 系统调用打开文件时,内核将会使用 inode 结构的 i_fop 字段赋值给 file 结构的 f_op 字段。我们再来重温下赋值过程:

static struct file *
__dentry_open(struct dentry *dentry, 
              struct vfsmount *mnt, 
              truct file *f, 
              int (*open)(struct inode *, struct file *), 
              const struct cred *cred)
{
    ...
    // 文件对应的inode对象
    inode = dentry->d_inode;
    ...
    // 使用inode结构的i_fop字段赋值给file结构的f_op字段
    f->f_op = fops_get(inode->i_fop);
    ...
    return f;
}

总结

本文主要介绍了 虚拟文件系统 的基本原理,从分析中可以发现,虚拟文件系统使用了类似于面向对象编程语言中的接口概念。正是有了 虚拟文件系统,Linux 才能支持各种各样的文件系统。

以上就是细说|Linux虚拟文件系统原理的详细内容,更多请关注知识资源分享宝库其它相关文章!

版权声明

本站内容来源于互联网搬运,
仅限用于小范围内传播学习,请在下载后24小时内删除,
如果有侵权内容、不妥之处,请第一时间联系我们删除。敬请谅解!
E-mail:dpw1001@163.com

分享:

扫一扫在手机阅读、分享本文

发表评论
热门文章
  • 华为 Mate 70 性能重回第一梯队 iPhone 16 最后一块遮羞布被掀

    华为 Mate 70 性能重回第一梯队 iPhone 16 最后一块遮羞布被掀
    华为 mate 70 或将首发麒麟新款处理器,并将此前有博主爆料其性能跑分将突破110万,这意味着 mate 70 性能将重新夺回第一梯队。也因此,苹果 iphone 16 唯一能有一战之力的性能,也要被 mate 70 拉近不少了。 据悉,华为 Mate 70 性能会大幅提升,并且销量相比 Mate 60 预计增长40% - 50%,且备货充足。如果 iPhone 16 发售日期与 Mate 70 重合,销量很可能被瞬间抢购。 不过,iPhone 16 还有一个阵地暂时难...
  • 惠普新款战 99 笔记本 5 月 20 日开售:酷睿 Ultra / 锐龙 8040,4999 元起

    惠普新款战 99 笔记本 5 月 20 日开售:酷睿 Ultra / 锐龙 8040,4999 元起
    本站 5 月 14 日消息,继上线官网后,新款惠普战 99 商用笔记本现已上架,搭载酷睿 ultra / 锐龙 8040处理器,最高可选英伟达rtx 3000 ada 独立显卡,售价 4999 元起。 战 99 锐龙版 R7-8845HS / 16GB / 1TB:4999 元 R7-8845HS / 32GB / 1TB:5299 元 R7-8845HS / RTX 4050 / 32GB / 1TB:7299 元 R7 Pro-8845HS / RTX 2000 Ada...
  • 酷凛 ID-COOLING 推出霜界 240/360 一体水冷散热器,239/279 元

    酷凛 ID-COOLING 推出霜界 240/360 一体水冷散热器,239/279 元
    本站 5 月 16 日消息,酷凛 id-cooling 近日推出霜界 240/360 一体式水冷散热器,采用黑色无光低调设计,分别定价 239/279 元。 本站整理霜界 240/360 散热器规格如下: 酷凛宣称这两款水冷散热器搭载“自研新 V7 水泵”,采用三相六极马达和改进的铜底方案,缩短了水流路径,相较上代水泵进一步提升解热能力。 霜界 240/360 散热器的水泵为定速 2800 RPM 设计,噪声 28db (A)。 两款一体式水冷散热器采用 27mm 厚冷排,...
  • Nginx服务器的HTTP/2协议支持和性能提升技巧介绍

    Nginx服务器的HTTP/2协议支持和性能提升技巧介绍
    Nginx服务器的HTTP/2协议支持和性能提升技巧介绍 引言:随着互联网的快速发展,人们对网站速度的要求越来越高。为了提供更快的网站响应速度和更好的用户体验,Nginx服务器的HTTP/2协议支持和性能提升技巧变得至关重要。本文将介绍如何配置Nginx服务器以支持HTTP/2协议,并提供一些性能提升的技巧。 一、HTTP/2协议简介:HTTP/2协议是HTTP协议的下一代标准,它在传输层使用二进制格式进行数据传输,相比之前的HTTP1.x协议,HTTP/2协议具有更低的延...
  • 两个表格切换的快捷键是什么

    两个表格切换的快捷键是什么
    两个表格切换的快捷键是“ctrl+pageup”和“ctrl+pagedown”,按键盘上的“ctrl+pageup”键是向右切换表格,按“ctrl+pagedown”键是向左切换表格。 本教程操作环境:windows7系统、Microsoft Office Excel2010版、Dell G3电脑。 两个工作表之间切换是Ctrl+Tab,两个工作簿之间切换是Ctrl+PageUP和Ctrl+PageDown。 打开Excel表格,打开几个工作簿。 按键盘上的Ctrl+P...