Linux 虚拟文件系统(一)--初识inode

1. 前言

文件系统是一个操作系统必备的功能之一。当你想运行一个应用时,你需要加载一个可执行文件。当你想编辑一段文本并保存时,你需要写入一个文本文件。当你想查看一个张图片时,你需要打开一个图片文件。可以想象,没有文件系统功能的操作系统也只剩下一套空壳。

一个成熟的操作系统至少支持一种文件系统。Linux 作为最主流的开源操作系统,只支持一种文件系统这么掉价的行为。肯定是不为 Linux 拥趸们接受的。

为了支持多个文件系统实现,以及访问其他操作系统的文件系统。Linux 在用户进程和文件系统实现之间加入了一个抽象层。这个抽象层被称为虚拟文件系统(Virtual File System,简称 VFS)。

计算机科学中的所有问题都可以通过增加一个间接层来解决 David Wheeler(剑桥大学计算机科学教授)

2. VFS 简介

VFS 屏蔽了底层文件系统实现的差异,为文件系统使用者提供了统一的文件系统视图。VFS 定义了一系列的通用接口和通用数据结构。支持加载和卸载文件系统,通用的文件操作等。

VFS 为了实现通用的文件视图,提供了一系列数据结构供用户进程和文件系统实现进行交互。这些对象包括 inode 结构体、超级块、目录项缓存、文件系统等。本文主要介绍 inode 结构体,以及其组织方式。

3. inode 结构体

ruby 复制代码
root@ubuntu:~# tree dir/
dir/
├── a.txt
└── subDir
    └── b.txt

1 directory, 2 files

一般而言,文件系统中文件的组织方式是有层次的。而实现这种层次结构的关键就是 inode 结构体。

在详细介绍 inode 结构体之前,我们先介绍一下 Linux 世界的一个重要法则:万物皆文件。在 Linux 的所有的 I/O 操作都可以通过 VFS 提供的读写 API 进行操作。

inode 存储的信息可以分成两大类

  • 描述文件状态的元数据。包括访问权限、创建时间、修改时间、文件长度等。
  • 保存实际文件内容的数据段(或指向数据的指针),数据段的具体格式取决于各个具体的文件系统实现

这里需要特别指出的是文件名并不存储在 inode 中。文件名存储在目录项(dentry)中,目录项存储在 inode 的数据段中,格式取决于具体的文件系统实现。

将文件名存储到目录项(dentry)而非 inode 中。可以很方便实现硬链接。

arduino 复制代码
struct inode {
	umode_t			i_mode; // 文件权限和文件类型的值
	kuid_t			i_uid; // 文件所属用户
	kgid_t			i_gid; // 文件所属组
	const struct inode_operations	*i_op; // inode 的操作函数
	struct super_block	*i_sb; // 文件系统的超级块
	unsigned long		i_ino; // inode 编号
	loff_t			i_size; // 文件长度
	struct timespec64	__i_atime; // 最后访问时间
	struct timespec64	__i_mtime; // 最后修改时间
	struct timespec64	__i_ctime; // inode 最后修改时间
	u8			i_blkbits; // 文件系统块的大小
	blkcnt_t		i_blocks; // 文件总共有多少块
	struct address_space	i_data; // 数据指针
	...
}

在 VFS 的世界观里是不区分目录和文件的,它们都统一使用了 inode 这个结构体进行表示。而目录和文件的 inode 区别在于数据段和 i_mode 字段。

文件的 i_data 数据指针指向的内存区域存储的是文件的内容。而目录的 i_data 数据指针指向的内存区域存储的是目录项,目录项的格式由各个文件系统自行定义, VFS 并没有做规范。

i_mode 是一个位掩码,用来标识文件类型和权限。Linux 内核中定义了如下的文件类型常量。

  • S_IFREG:常规文件
  • S_IFDIR:目录
  • S_IFCHR:字符设备
  • S_IFBLK:块设备
  • S_IFIFO:管道或FIFO
  • S_IFLNK:符号链接
  • S_IFSOCK:套接字

当一个 inode 结构表示目录的时候,i_mode 位掩码对应的位置会置为 S_IFDIR。内核代码可以轻易地判断出一个 inode 结构是属于文件还是一个目录。

上图是本小节 tree 命令的执行目录的 inode 结构示意图。其中圆形的表示 inode 结构,方形的表示数据段。每个 inode 都有一个文件系统内唯一的编号 i_no 和一个指向数据段的指针。数据段的内容因为文件类型而不同。如果是文件则为数据段的内容为文件内容。如果是目录则为目录项数据。目录项数据取决于具体的文件系统实现,但是至少应该包含文件名和 inode 编码。

4. inode 组织方式

内核中每个的文件系统的 inode 都以链表的形式组织在一起。每个 inode 结构都通过自身的 i_list 字段指向排在自己后面的 inode 结构。首尾相连,组成一个庞大的 inode 链表。

但是,链表的查找速度又太慢了。所以 VFS 又将所有的 inode 结构组织到一张哈希表中。通过溢出链的方式处理哈希冲突。inode 结构体中的 i_hash 字段用于关联溢出链的每个 inode 结构。

4. 参考

相关推荐
NiNg_1_2343 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
Chrikk4 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*4 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue4 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man4 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
customer086 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
Yaml47 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
小码编匠8 小时前
一款 C# 编写的神经网络计算图框架
后端·神经网络·c#