Linux虚拟文件系统(2)

news2025/5/26 3:26:41
2.3 目录项-dentry

       目录项,即 dentry,用来记录文件的名字、索引节点指针以及与其他目录项的关联关系。多个关联的目录项,就构成了文件系统的目录结构。和上一章中超级块和索引节点不同,目录项并不是实际存在于磁盘上的,在使用的时候在内存中创建目录项对象,目录项是由内核维护的一个内存数据结构,所以通常也被叫做目录项缓存。其实通过索引节点已经可以定位到指定的文件,但是索引节点对象的属性非常多,在查找,比较文件时,直接用索引节点效率不高,所以引入了目录项的概念。

目录项的目的就是提高文件查找,比较的效率,所以访问过的目录项都会缓存在slab中。

slab中缓存的名称一般就是 dentry,可以通过如下命令查看:

dentry的相关属性结构

/* 目录项对象结构 */
struct dentry {
    atomic_t d_count;       /* 使用计数:跟踪该目录项被引用的次数。当d_count变为0时,表示没有其他部分在使用这个目录项,可以考虑释放它。 */
    
    unsigned int d_flags;   /* 目录项标识:用于标记目录项的状态或特性,如是否为负目录项(即不存在的文件)等。 */
    
    spinlock_t d_lock;      /* 单目录项锁:一个自旋锁,用于保护对这个目录项进行并发访问时的数据一致性。 */
    
    int d_mounted;          /* 是否登录点的目录项:指示该目录项是否是一个挂载点。如果是,则值大于0,表示在其下有挂载的文件系统。 */
    
    struct inode *d_inode;  /* 相关联的索引节点:指向与该目录项相关的inode结构体。如果d_inode为NULL,表示这是一个负目录项。 */
    
    struct hlist_node d_hash;/* 散列表:用于将目录项链接到哈希表中,以便快速查找。 */
    
    struct dentry *d_parent;/* 父目录的目录项对象:指向上一级目录的目录项。根目录的d_parent指向自身。 */
    
    struct qstr d_name;     /* 目录项名称:包含目录项的名字及其长度和哈希值。 */
    
    struct list_head d_lru; /* 未使用的链表:当目录项不再被使用时,会被加入到LRU链表中,等待回收。 */
    
    /*
     * d_child and d_rcu can share memory
     */
    union {
        struct list_head d_child; /* child of parent list: 将当前目录项链接到父目录的子目录项链表中。 */
        struct rcu_head d_rcu;    /* RCU机制头:用于RCU(Read-Copy Update)机制的支持,保证删除过程中的读写安全。 */
    } d_u;
    
    struct list_head d_subdirs; /* 子目录链表:用于维护该目录下的所有子目录项,形成树状结构。 */
    
    struct list_head d_alias;   /* 索引节点别名链表:将所有具有相同inode的目录项链接在一起,因为多个路径可能指向同一个inode。 */
    
    unsigned long d_time;       /* 重置时间:记录了最近一次对该目录项执行某些操作的时间,比如更新其状态等。 */
    
    const struct dentry_operations *d_op; /* 目录项操作相关函数:指向一组函数,这些函数定义了如何操作目录项。 */
    
    struct super_block *d_sb;   /* 文件的超级块:指向该目录项所属文件系统的超级块结构。 */
    
    void *d_fsdata;              /* 文件系统特有数据:提供给具体文件系统使用,可用于存储特定于该文件系统的额外信息。 */
    
    unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* 短文件名:内联存储的短文件名,用于加速访问。如果文件名较长,则不会使用此字段。 */
};

       dentry有两个特殊的成员变量d_lrud_hash。这其实关联于两张dentry cache表,顾名思义,该表保存的是一系列已分配过的dentry的缓存池,用于文件操作中快速的查找和使用。

LRU表

  • 作用:LRU(Least Recently Used)表是一个链表,用于管理最近最少使用的dentry对象。当系统需要回收内存时,会优先从LRU表中选择最久未使用的dentry进行释放
  • 操作:当一个dentry的引用计数变为0时,它会被加入到LRU表中。

散列表

  • 作用:hash散列表用于快速查找dentry对象。每个dentry根据其名称和父目录计算出一个哈希值,并插入到相应的散列桶中。
  • 操作:当需要查找一个文件或目录时,系统会根据路径名计算哈希值,并在散列表中查找对应的dentry。

这两个列表之间会产生复杂的关系:

  • 引用为 0:一个在散列表中的 dentry 变成没有人引用了,就会被加到 LRU 表中去;
  • 再次被引用:如果一个之前未被使用的dentry再次被引用,其引用计数会增加,并从LRU表中移除,表示它又变成了一个活跃的dentry;
  • 分配:当 dentry 在散列表中没有找到,则从 Slub 分配器中分配一个;
  • 过期归还:当 LRU 表中最长时间没有使用的 dentry 应该释放回 Slub 分配器;
  • 文件删除:如果该dentry的引用计数为0,则可以直接从缓存中移除;否则,需要等待所有引用被释放后再进行处理。文件被删除了,相应的 dentry 应该释放回 Slub 分配器;
  • 结构复用:为了提高效率,Linux内核会尽量复用已经存在的dentry结构体,而不是每次都创建新的实例。当一个dentry的引用计数变为0时,它会被标记为可复用,并在下次需要时重新初始化。
/* 目录项相关操作函数 */
struct dentry_operations {
    /* 该函数判断目录项对象是否有效。VFS准备从dcache中使用一个目录项时会调用这个函数 */
    int (*d_revalidate)(struct dentry *, struct nameidata *);
    /* 为目录项对象生成hash值 */
    int (*d_hash) (struct dentry *, struct qstr *);
    /* 比较 qstr 类型的2个文件名 */
    int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);
    /* 当目录项对象的 d_count 为0时,VFS调用这个函数 */
    int (*d_delete)(struct dentry *);
    /* 当目录项对象将要被释放时,VFS调用该函数 */
    void (*d_release)(struct dentry *);
    /* 当目录项对象丢失其索引节点时(也就是磁盘索引节点被删除了),VFS会调用该函数 */
    void (*d_iput)(struct dentry *, struct inode *);
    char *(*d_dname)(struct dentry *, char *, int);
};
2.4 文件对象·-file

       在Linux内核中,文件对象(file结构体)是用于表示一个打开的文件实例的核心数据结构。每当用户空间的应用程序通过系统调用(如open())打开一个文件时,内核会创建一个file结构体对象来跟踪该文件的所有相关信息。

file的相关属性结构

struct file {
    union {
        struct llist_node fu_llist; // 用于链表操作,支持轻量级锁机制
        struct rcu_head fu_rcuhead; // RCU(Read-Copy Update)机制头,用于安全删除
    } f_u;

    struct path f_path;           /* 文件路径信息,包括挂载点和目录项 */
    struct inode *f_inode;        /* 指向与该文件关联的inode结构体 */
    const struct file_operations *f_op; /* 文件操作函数指针,定义了对该文件可以执行的操作 */

    spinlock_t f_lock;            /* 自旋锁,用于保护对文件对象的并发访问 */
    
    atomic_long_t f_count;        /* 使用计数,表示当前有多少地方在使用这个文件对象 */
    
    unsigned int f_flags;         /* 打开文件时指定的标志位,如O_RDONLY, O_WRONLY等 */
    
    fmode_t f_mode;               /* 访问模式,指示文件是只读、只写或两者皆可 */
    
    loff_t f_pos;                 /* 当前文件指针位置,即下一次读写操作将从哪里开始 */
    
    struct fown_struct f_owner;   /* 用于实现信号发送机制,允许进程接收特定事件的通知 */
    
    struct cred *f_cred;          /* 凭证信息,包含打开文件时的安全凭证,如UID和GID */
    
    struct file_ra_state f_ra;    /* 读取提前(readahead)状态,有助于预读取文件内容以提高性能 */
    
    u64 f_version;                /* 版本号,每次修改都会增加,可用于检测文件对象的变化 */
    
    void *private_data;           /* 私有数据,通常由设备驱动程序使用,存储驱动需要的状态信息 */
    
    struct list_head f_ep_links;  /* epoll相关的链表,用于异步I/O通知 */
    
    struct address_space *f_mapping;/* 映射到的地址空间,用于管理文件映射到内存的情况 */
};
  1. 文件对象表示进程已打开的文件,从用户角度来看,我们在代码中操作的就是一个文件对象。
  2. 根据上述结构中的struct path f_path知道文件对象反过来指向一个目录项对象(根据struct dentry结构体知道目录项反过来指向一个索引节点)
  3. 其实只有目录项对象才表示一个已打开的实际文件,虽然一个文件对应的文件对象不是唯一的,但其对应的索引节点和目录项对象却是唯一的。

补充:

  •  一个文件可以被多个进程以不同的方式打开,因此可能存在多个文件对象对应同一个实际文件。
  •  尽管一个文件可能有多个文件对象(因为被多次打开),但它的目录项对象是唯一的,因为它代表了一个具体的文件名及其在文件系统树中的位置。
  • 对于每一个实际的文件而言,无论它被多少次打开,其索引节点都是唯一的。这意味着即使有多个文件对象和目录项对象指向同一个文件,它们都将共享同一个索引节点。
  • 目录项对象才表示一个已打开的实际文件。它不仅仅是一个文件的名称,而是包括了该文件在文件系统中的完整路径信息,并直接关联到具体的索引节点。因此,当提到“实际文件”的时候,我们实际上是指文件系统中某个具体位置上的那个文件,而这个位置是由目录项对象唯一确定的。即使有多个文件对象(由于多次打开同一个文件),它们都指
  • 向同一个目录项对象,进而指向同一个索引节点

3.示例

       (1)例如 打开/data/test.txt

  1. 📁 VFS沿路径逐级查找dentry缓存

  2. 💾 未命中则从磁盘加载inode并创建dentry

  3. 📝 创建file结构体,绑定进程fd与目标inode

    (2) 例如 向/data/test.txt写入文件时

  1. ✍️ 通过file→inode找到数据块地址
  2. 🔄 修改inode中的文件大小和更新时间
  3. 📌 超级块同步记录文件系统状态变化 

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

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

相关文章

【数据结构】栈和队列(上)

目录 一、栈(先进后出、后进先出的线性表) 1、栈的概念及结构 2、栈的底层结构分析 二、代码实现 1、定义一个栈 2、栈的初始化 3、入栈 3、增容 4、出栈 5、取栈顶 6、销毁栈 一、栈(先进后出、后进先出的线性表) 1、…

科技赋能·长效治理|无忧树建筑修缮渗漏水长效治理交流会圆满举行!

聚焦行业痛点,共话长效未来!5月16日,由无忧树主办的主题为“科技赋能长效治理”的建筑修缮渗漏水长效治理技术交流会在上海圆满举行。来自全国的建筑企业代表、专家学者、技术精英齐聚一堂,共探渗漏治理前沿技术,见证科…

【闲聊篇】java好丰富!

1、在学习mybatis-plus的文档时,发现引入了solon依赖,才发现这是一个对标spring生态的框架,有意思! 还有若依框架,真的好丰富~~~~~~~ 2、今天面试官问我,他说很少遇到用redission做延迟队列的。后面我就反…

6.3.2图的深度优先遍历

知识总览: 树的先根遍历: 采用递归一直找某个节点的子树直到找不到从上往下找 访问根节点1,1的子树有2、3、4,访问2,2节点子树有5访问5,5没有子树,退回到2,2还有子树6访问6,6没有子树再退回到2,2的子树都被访问了再退…

畅游Diffusion数字人(30):情绪化数字人视频生成

畅游Diffusion数字人(0):专栏文章导航 前言:仅从音频生成此类运动极具挑战性,因为它在音频和运动之间存在一对多的相关性。运动视频的情绪是多元化的选择,之前的工作很少考虑情绪化的数字人生成。今天解读一个最新的工作FLOAT,可以生成制定情绪化的数字人视频。 目录 贡献…

UE5 Va Res发送请求、处理请求、json使用

文章目录 介绍发送一个Get请求发送Post请求设置请求头请求体带添json发送请求完整的发送蓝图 处理收到的数据常用的json处理节点 介绍 UE5 自带的Http插件,插件内自带json解析功能 发送一个Get请求 只能写在事件图表里 发送Post请求 只能写在事件图表里 设置…

【读代码】BAGEL:统一多模态理解与生成的模型

一、项目概览 1.1 核心定位 BAGEL是字节跳动推出的开源多模态基础模型,具有70亿激活参数(140亿总参数)。该模型在统一架构下实现了三大核心能力: 多模态理解:在MME、MMBench等9大评测基准中超越Qwen2.5-VL等主流模型文本生成图像:生成质量媲美SD3等专业生成模型智能图像…

隧道自动化监测解决方案

行业现状 隧道作为一种重要的交通运输通道,不管是缓解交通压力,还是让路网结构更趋于完善,它都有着不可估量的作用。隧道在运营过程中,由于受到材料退化、地震、人为因素等影响会发生隧道主体结构的损坏和劣化。若不及时检修和维护…

游戏引擎学习第307天:排序组可视化

简短谈谈直播编程的一些好处。 上次结束后,很多人都指出代码中存在一个拼写错误,因此这次我们一开始就知道有一个 bug 等待修复,省去了调试寻找错误的时间。 今天的任务就是修复这个已知 bug,然后继续排查其他潜在的问题。如果短…

java接口自动化初识

简介 了解什么是接口和为什么要做接口测试。并且知道接口自动化测试应该学习哪些技术以及接口自动化测试的落地过程。 一、什么是接口 在这里我举了一个比较生活化的例子,比如我们有一台笔记本,在笔记本的两端有很多插口。例如:USB插口。那…

NVM安装使用及问题解决

目录 一、前言 二、NVM安装 三、配置下载源 四、nvm使用 五、安装nvm list available没有的版本 六、问题解决 一、前言 如果你开发 Node.js 项目,可能会遇到这些问题: ①新项目需要 Node.js 18,但老项目只能用 Node.js 14,…

C++学习之STL学习:string类使用

在之前的学习中,我们初步了解到了STL的概念,接下来我们将深入学习STL中的string类的使用,后续还会结合他们的功能进行模拟实验 目录 为什么要学习string类? 标准库中的string类 string类(了解) auto和范围…

5月24日day35打卡

模型可视化与推理 知识点回顾: 三种不同的模型可视化方法:推荐torchinfo打印summary权重分布可视化进度条功能:手动和自动写法,让打印结果更加美观推理的写法:评估模式 作业:调整模型定义时的超参数&#x…

Linux(7)——进程(概念篇)

目录 一、基本概念 二、描述进程——PCB 1.task_struct——PCB的一种 2.task_struct的内容分类 三、查看进程 1.通过系统目录查看 2.通过ps命令查看 四、通过系统调用获取进程的PID和PPID 五、通过系统调用创建进程 1.fork函数创建子进程 2.使用if来引出问题 六、L…

前端流行框架Vue3教程:24.动态组件

24.动态组件 有些场景会需要在两个组件间来回切换&#xff0c;比如 Tab 界面 我们准备好A B两个组件ComponentA ComponentA App.vue代码如下&#xff1a; <script> import ComponentA from "./components/ComponentA.vue" import ComponentB from "./…

Unity3D仿星露谷物语开发48之显示树桩效果

1、目标 砍完橡树之后会露出树桩&#xff0c;然后树桩可以用斧头收割&#xff0c;并将创建一个新的砍树桩的粒子效果。 这里有&#xff1a;一种作物收获后创造另一种作物的逻辑。 2、分析 在SO_CropDetailsList中&#xff0c;Harvested Transform Item Code可以指定收获后生…

[Datagear] 实现按月颗粒度选择日期的方案

在使用 Datagear 构建数据分析报表时,常常会遇到一个问题:如果数据的目标颗粒度是“月”,默认的日期控件却是精确到“日”的,这在用户交互和数据处理层面会带来不必要的复杂度。本文将分享两种解决方案,帮助你更好地控制日期控件的颗粒度,实现以月为单位的日期筛选功能。…

漏洞检测与渗透检验在功能及范围上究竟有何显著差异?

漏洞检测与渗透检验是确保系统安全的重要途径&#xff0c;这两种方法各具特色和功效&#xff0c;它们在功能上有着显著的差异。 目的不同 漏洞扫描的主要任务是揭示系统内已知的安全漏洞和隐患&#xff0c;这就像是对系统进行一次全面的健康检查&#xff0c;看是否有已知的疾…

DB-GPT扩展自定义Agent配置说明

简介 文章主要介绍了如何扩展一个自定义Agent&#xff0c;这里是用官方提供的总结摘要的Agent做了个示例&#xff0c;先给大家看下显示效果 代码目录 博主将代码放在core目录了&#xff0c;后续经过对源码的解读感觉放在dbgpt_serve.agent.agents.expand目录下可能更合适&…

家政维修平台实战09:推送数据到多维表格

目录 1 API调试2 创建云函数3 前端调用整体效果总结 上一篇我们搭建了服务分类的后台功能&#xff0c;对于分类的图标通过集成TOS拿到了可以公开访问的地址&#xff0c;本篇我们将写入的数据推送至多维表格中。 1 API调试 要想推送多维表格的数据&#xff0c;首先要利用官方的…