文件系统之inode

article/2025/10/2 22:58:55

注:本文分析基于linux-4.18.0-193.14.2.el8_2内核版本,即CentOS 8.2

1 inode

内存中,每个文件都有一个inode,一切皆文件,其实应该是说一切皆inode。inode保存了文件系统中一个文件的属性描述,比如文件类型,文件权限,属主,文件创建、读取或修改的时间等等。除了内存中的inode,磁盘中也有对应的inode结构,比如ext4文件系统中,内存中的inode是struct ext4_inode_info,而磁盘中的inode是struct ext4_inode_info,今天我们主要分析下内存中的inode结构。

2 inode主要成员变量

struct inode {umode_t			i_mode;  //文件类型和访问权限unsigned short		i_opflags;kuid_t			i_uid; //inode所属文件的所有者idkgid_t			i_gid; //inode所属文件所在组idunsigned int		i_flags;...const struct inode_operations	*i_op; //该inode操作函数集合struct super_block	*i_sb;  //指向所属超级块对象struct address_space	*i_mapping; //指向inode在内存中的pagecache...unsigned long		i_ino;  //inode节点号 ls -i 可以查看文件inode号...struct timespec64	i_atime; //文件最后访问时间struct timespec64	i_mtime; //文件最后修改时间struct timespec64	i_ctime; //文件创建时间spinlock_t		i_lock;	/* i_blocks, i_bytes, maybe i_size */unsigned short          i_bytes; //文件在最后一块中的字节数unsigned int		i_blkbits; //block size(字节位数),一般为9enum rw_hint		i_write_hint;blkcnt_t		i_blocks; //文件的总块数,这里一个block为512 byte,也就是扇区大小...unsigned long		dirtied_when;	//inode变脏的时间unsigned long		dirtied_time_when;//通过该变量链接到全局inode哈希表inode_hashtable,用于inode的快速查找//哈希值通过超级块和inode number计算struct hlist_node	i_hash; //通过该变量挂载到bdi_writeback的b_io链表,等待BDI回写,也就是这是脏inodestruct list_head	i_io_list;	/* backing dev IO list *///该inode所在设备对应的bdi_writeback结构,用于inode的回写struct bdi_writeback	*i_wb;	...struct list_head	i_lru;	//通过该变量链接到超级块的s_inode_lru链表struct list_head	i_sb_list; //通过该变量链接到超级块的s_inodes上struct list_head	i_wb_list;	//通过该变量链接到超级块的s_inodes_wb上,回写统计union {//所有引用该inode的目录项将形成一个链表//比如文件被链接到其他的文件,就会有多个dentrystruct hlist_head	i_dentry; struct rcu_head		i_rcu;};...const struct file_operations	*i_fop;	//该inode对应的文件的操作函数集合struct file_lock_context	*i_flctx;struct address_space	i_data; //内嵌在inode的pagecache对象...void			*i_private; /* fs or device private pointer */RH_KABI_RESERVE(1)RH_KABI_RESERVE(2)
};

3 创建一个inode

我们以ext4的创建目录——mkdir为例,看下inode的创建过程,

const struct inode_operations ext4_dir_inode_operations = {.create		= ext4_create,....mkdir		= ext4_mkdir,...
};const struct file_operations ext4_dir_operations = {.llseek		= ext4_dir_llseek,.read		= generic_read_dir,....open		= ext4_dir_open,.release	= ext4_release_dir,
};static int ext4_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{...//调用ext4文件系统的alloc_inode函数,创建新的inodeinode = ext4_new_inode_start_handle(dir, S_IFDIR | mode,&dentry->d_name,0, NULL, EXT4_HT_DIR, credits);...//对于目录文件,i_op和i_fop指向的也是对应的目录操作函数//如果是普通文件,i_op和i_fop指向的会是file操作函数inode->i_op = &ext4_dir_inode_operations;inode->i_fop = &ext4_dir_operations;...
}

ext4_new_inode_start_handle通过以下调用路径,

ext4_new_inode_start_handle ->__ext4_new_inode ->new_inode 

最终在new_inode中将分配成功的inode挂到对应超级块的s_inodes链表上,这个链表就是这超级块所有的inode对象。

struct inode *new_inode(struct super_block *sb)
{struct inode *inode;spin_lock_prefetch(&sb->s_inode_list_lock);//调用对应文件系统的alloc_inode方法创建一个inodeinode = new_inode_pseudo(sb);if (inode)//将该inode挂载到对应超级块的s_inodes链表上inode_sb_list_add(inode);return inode;
}

4 添加inode到inode cache链表

超级块上除了s_inodes链表,还有一个LRU链表s_inode_lru,这放的是未使用,或者干净的inode,比如回写完毕的inode就会挂载到这个链表上,这个链表也称为inode cache,在系统需要回收内存时,就会对这个链表下手,回收最近最少使用的inode。

添加到s_inode_lru链表的路径主要有两个:

  • 回写完毕的inode,也就是干净的inode
  • inode没有其他进程引用

不过最终都是通过inode_lru_list_add将inode挂载到s_inode_lru链表,

static void inode_lru_list_add(struct inode *inode)
{//将inode挂载到超级块的s_inode_lru链表if (list_lru_add(&inode->i_sb->s_inode_lru, &inode->i_lru))this_cpu_inc(nr_unused);elseinode->i_state |= I_REFERENCED;
}

先看回写完毕的inode,

inode_sync_complete ->inode_add_lru ->inode_lru_list_add

然后是无其他进程引用时,

iput ->iput_final ->inode_add_lru ->inode_lru_list_add

5 从inode cache中删除inode(回收inode cache)

上面我们提到系统回收内存时,会对操作s_inode_lru链表,我们也大概看下,

long prune_icache_sb(struct super_block *sb, struct shrink_control *sc)
{LIST_HEAD(freeable);long freed;//遍历超级块的s_inode_lru链表,按照回收控制结构sc指定的回收数量,//将可回收的inode隔离到freeable链表中集中回收freed = list_lru_shrink_walk(&sb->s_inode_lru, sc,inode_lru_isolate, &freeable);//将隔离出来的inode进行回收,这样隔离后可以避免锁竞争dispose_list(&freeable);return freed;
}

回收inode主要是要从几个链表中抽离,和脏数据回写

  • 超级块的s_inode_lru链表
  • bdi_writeback的b_io链表
  • 超级块的s_inodes链表
  • 回写pagecache
  • 全局inode哈希表
static void dispose_list(struct list_head *head)
{while (!list_empty(head)) {struct inode *inode;inode = list_first_entry(head, struct inode, i_lru);//将inode从超级块的s_inode_lru链表摘除list_del_init(&inode->i_lru);//回收inodeevict(inode);cond_resched();}
}static void evict(struct inode *inode)
{const struct super_operations *op = inode->i_sb->s_op;BUG_ON(!(inode->i_state & I_FREEING));BUG_ON(!list_empty(&inode->i_lru));//从bdi_writeback的b_io链表摘除if (!list_empty(&inode->i_io_list))inode_io_list_del(inode);//将inode从超级块的s_inodes链表摘除inode_sb_list_del(inode);//等待该inode回写完毕inode_wait_for_writeback(inode);//调用对应文件系统的evict_inode方法,回写pagecacheif (op->evict_inode) {op->evict_inode(inode);} else {truncate_inode_pages_final(&inode->i_data);clear_inode(inode);}//如果是块设备inodeif (S_ISBLK(inode->i_mode) && inode->i_bdev)bd_forget(inode);//如果是字符型设备if (S_ISCHR(inode->i_mode) && inode->i_cdev)cd_forget(inode);//从全局inode哈希表中摘除remove_inode_hash(inode);...//回收inodedestroy_inode(inode);
}

处理完这些引用后,就可以调用destroy_inode回收到slab缓存,对于ext4,调用的是ext4_destroy_inode,

static void destroy_inode(struct inode *inode)
{BUG_ON(!list_empty(&inode->i_lru));__destroy_inode(inode);//调用对应文件系统的destroy_inode方法,将inode回收到slab缓存//对于ext4,调用的是ext4_destroy_inodeif (inode->i_sb->s_op->destroy_inode)inode->i_sb->s_op->destroy_inode(inode);elsecall_rcu(&inode->i_rcu, i_callback);
}static void ext4_destroy_inode(struct inode *inode)
{	if (!list_empty(&(EXT4_I(inode)->i_orphan))) {...}//调用ext4_i_callback将inode释放会slab缓存call_rcu(&inode->i_rcu, ext4_i_callback);
}static void ext4_i_callback(struct rcu_head *head)
{struct inode *inode = container_of(head, struct inode, i_rcu);//释放回slab缓存kmem_cache_free(ext4_inode_cachep, EXT4_I(inode));
}

6 结构关系

  • 一块磁盘,三个分区,sda1和sda2是ext4文件系统,sda3是xfs文件系统
  • 全局超级块链表super_blocks将三个超级块串联在一起
  • sda1上的ext4_inode_info结构中内嵌inode结构,其中i_sb指向对应的超级块
  • A、B、C三个inode挂载到超级块s_nodes链表,A,B两个未使用的inode还会挂载到s_inode_lru链表
  • sda2上的ext4文件系统同sda1一样
  • 而sda3上的xfs除了对应的xfs_inode结构不同,大体结构也是一样的,这其实就是VFS的作用,对所有文件系统抽象了一层
    在这里插入图片描述

http://chatgpt.dhexx.cn/article/g2O2sSRA.shtml

相关文章

内核中inode结构体

在介绍inode结构体之前先做一个链接文件的实验: 1.创建一个普通的文件test.txt,并写入内容查看,如下 2.创建test.txt的硬链接文件,并测试如下: 3.创建test.txt的软连接文件并测试,如下: …

linux inode大小,Linux inode详解

Sam最近在架设spnfs过程中发现。在client上创建文件,文件名以及目录关系都可以在MDS上表现出来。文件内容则存储在DS上。但DS上的文件名却都是由数字组成的。命名方式为:inode.xxxx. 关于inode,Sam刚开始以为是kernel的node概念。后来查了一下…

理解inode

文章目录 磁盘构成磁盘存储的映射 inode和data block(重点)inode bitmapblock bitmapinode tabledata table 查找文件的示意图(重点)创建新文件需要的操作(重点)为什么明明还有空间,却创建不了文…

Linux inode

之前写了一篇Linux的文件描述符博客,是针对打开的文件来说的。那么如果一个文件没有被打开呢?存在哪里呢?又怎么存储?今天让我们一起来坐上"贼船"到知识的海洋中学习吧~ 目录 磁盘 什么是磁盘? 磁盘vs线…

inode结点

一、 机械硬盘 1.1 工作原理 写入时,磁头线圈加电,在周围产生磁场,磁化其下的磁性材料;电流的方向不同,所以磁场的方向也不同,可以表示 0 和 1 的区别。——“电生磁”。 读取时,磁头线圈&am…

什么是inode?

理解inode,要从文件储存说起。文件储存在硬盘上,硬盘的最小存储单位叫扇区(Sector)。每个扇区储存512字节(相当于0.5KB)。操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区&…

inode详解

文章目录 什么是inode?inode的内容(inode是一个结构体)block区和inode区的理解inode编号硬链接软连接 什么是inode? 理解inode,要从文件储存说起。 文件储存在硬盘上,硬盘的最小存储单位叫做"扇区&quo…

代码编写流程

文章目录 代码编写步骤好的代码满足条件:注意事项 代码编写步骤 1、分析任务目标,根据目标选择合适的代码设计模式   1.1、创建模式选择   1.2、结构模式选择   1.3、行为模式选择 2、整理思路、画出思维导图、或代码流程图 3、找到其中的关键对象…

代码详解设计模式--模板模式

模板模式 一个抽象类公开定义了执行方法的模板,它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。 优点: 封装不变部分,扩展可变部分。提取公共代码,便于维护。行为由父类控制,子类实现…

GRID-FORM 基于 VUE3 可视化低代码表单设计器

本文开源代码在 GRID-FORM,如能帮到您麻烦给个星哈 楔子 大概 4 年前,我做了一个简单的动态表单功能,开发人员通过 UI 界面配置表单(其实就是添加常用的控件,如文本框、下拉框等)就能向用户提供数据查询&a…

领域驱动实践总结(基本理论总结与分析+架构分析与代码设计+具体应用设计分析V)

目录 领域驱动实践总结三:具体应用设计分析 一、应用项目的基本背景 二、针对项目进行领域驱动的战略设计阶段 (一)事件风暴确定产品愿景 (二)事件风暴进行业务场景分析 场景分析一:请假 用户:…

领域驱动实践总结(基本理论总结与分析+架构分析与代码设计V+具体应用设计分析)

目录 领域驱动实践总结二:架构分析与代码设计 一、微服务架构模型的对比与选择 (一)整洁架构 (二)六边形架构 (三)DDD 分层架构 1.用户接口层 2.应用层 3.领域层 4.基础层 5.从三层架构向 DDD 分…

十二种常见设计模式代码详解

零:设计模式分类 设计模式有创建型模式、结构型模式与行为型模式 创建型:单例模式、工厂模式(简单工厂,工厂方法,抽象工厂)结构型:适配器模式、门面模式、装饰器模式、注册树模式、代理模式、…

优雅代码的秘密,都藏在这6个设计原则中

优雅的代码,犹如亭亭玉立的美女,让人赏心悦目。而糟糕的代码,却犹如屎山,让人避而远之。 如何写出优雅的代码呢?那就要理解并熟悉应用这6个设计原则啦:开闭原则、单一职责原则、接口隔离原则 、迪米特法则…

低代码--低代码开发(LCDP)介绍

低代码开发(LCDP)介绍 1 介绍1.1 概述1.2 行业风向1.3 行业报告1.4 优点减少重复编程避免沟通隔阂提升效率 1.5 挑战完全抛弃代码的代价,就是平台能力与灵活性受限应用低代码平台阻力大技术局限老旧系统改造困难职业角色缺失应用者大多是技术…

网页设计个人主页代码

/ 01 / 前话 主题《周末の守候》采用Dreamweaver软件制作,主题包含了12页,页面能够相互跳转,运用了HTML5标签,DIVCSS布局,网站主题鲜明、内容丰富、健康、高雅且栏目设置合理,网站中页面色彩搭配合理&…

#低码系列#如何设计一个低代码平台?

低码系列文章 #低码系列#低代码来了,程序员会失业吗? 整体设计 用户群体 对于基础功能的实现,不需要开发人员介入。业务人员通过可视化页面,即可完成设计。从这个角度上看,低码平台面向的用户是业务人员、系统管理…

浅谈代码结构的设计

本文来自网易云社区 作者:陆秋炜 引言 :很久之前,在做中间件测试的时候,看到开发人员写的代码,有人的代码,看起来总是特别舒服,但有的开发代码,虽然逻辑上没有什么问题,但总给人感觉特别难受。后来成为了一位专职开发人员,渐渐发现,自己的代码也是属于“比较难受”…

领域驱动实践总结(基本理论总结与分析V+架构分析与代码设计+具体应用设计分析)

目录 领域驱动实践总结一:基本理论总结与分析 一、领域驱动设计两大设计:战略设计和战术设计 (一)战略设计 1.出发角度与目标 2.实现方式:事件风暴与模型确立(用例分析、场景分析和用户旅程分析) 3.用三步来划定…

如何设计一个低代码平台

编者按:近些年来,低代码发展火热,各种低代码平台如雨后春笋纷纷崛起,这些平台各定位不同,优劣不同,用户的选择空间很大。那么,如果用户想从零开始设计一个低代码平台,该如何做呢&…