目录

1.知识回顾

2.Ext2文件系统

Block group

Data Block

inode table

★inode

★注意: EXT2文件系统的inode编号是有限的

重点解释i_block[EXT2_N_BLOCKS]

直接索引

间接索引(一级间接索引)

二级间接索引

三级间接索引

三个级别的索引的关系图

特别提醒


1.知识回顾

参见OS34.【Linux】文件系统前置知识文章回顾

2.Ext2文件系统

文件系统的通俗解释: 文件系统是把硬盘上的数据整理得井井有条,让你存、找、删文件都方便的一套“管理规则

下面以Ext2文件系统为例:

《Understanding The Linux Kernel》CHAPTER 18 The Ext2 and Ext3 Filesystems给了这样一张图:Ext2 Disk Data Structures

解释这几个区域的含义:

Block group

大磁盘划分为小分区, 小分区再划分为一个个的块组(block group), 如果块组block group管好了,那么小分区就管好了,那么大磁盘就管理好了

Boot block: 是启动块,含操作系统启动(boot)信息,一般在整个磁盘的开头,如果Boot block丢失或者损坏,操作系统将无法启动,需要主引导修复工具

Data Block

Data Block数据块存放文件内容,是文件系统主要管理的部分,Data Block以块的形式出现,常见的是4KB的大小,而且读写文件以块为单位

而且数据块占绝大部分空间,在NMU大学的《A Non-Technical Look Inside the EXT2 File System》文章中的图可以清楚说明:

inode table

inode table,顾名思义,是inode表,是存储inode的一个线性数组

https://www.kernel.org/doc/html/latest/filesystems/ext4/inodes.html内核文档中有说明:

★inode

全称是(index node,即索引结点)存放单个文件是所有属性(文件的属性不含有文件的名称!!!),128字节→n个文件就有n个inode,且每个inode都有唯一的编号

Linux系统中,标识文件用的是inode编号,因为每个inode的编号是独一无二的

注意: 1.inode的设置是以分区为单位的,不能跨分区,因为每个分区都含自己独立的文件系统

         2. inode表示文件的所有属性,文件名,并不属于inode自己的属性

例如ls -li的i就表示显示每个文件的inode编号

Linux内核的/fs/ext2.h中包含ext2_inode结构体(磁盘上的inode)代码

/*
 * Structure of an inode on the disk
 */
struct ext2_inode {
	__le16	i_mode;		/* File mode */
	__le16	i_uid;		/* Low 16 bits of Owner Uid */
	__le32	i_size;		/* Size in bytes */
	__le32	i_atime;	/* Access time */
	__le32	i_ctime;	/* Creation time */
	__le32	i_mtime;	/* Modification time */
	__le32	i_dtime;	/* Deletion Time */
	__le16	i_gid;		/* Low 16 bits of Group Id */
	__le16	i_links_count;	/* Links count */
	__le32	i_blocks;	/* Blocks count */
	__le32	i_flags;	/* File flags */
	union {
		struct {
			__le32  l_i_reserved1;
		} linux1;
		struct {
			__le32  h_i_translator;
		} hurd1;
		struct {
			__le32  m_i_reserved1;
		} masix1;
	} osd1;				/* OS dependent 1 */
	__le32	i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
	__le32	i_generation;	/* File version (for NFS) */
	__le32	i_file_acl;	/* File ACL */
	__le32	i_dir_acl;	/* Directory ACL */
	__le32	i_faddr;	/* Fragment address */
	union {
		struct {
			__u8	l_i_frag;	/* Fragment number */
			__u8	l_i_fsize;	/* Fragment size */
			__u16	i_pad1;
			__le16	l_i_uid_high;	/* these 2 fields    */
			__le16	l_i_gid_high;	/* were reserved2[0] */
			__u32	l_i_reserved2;
		} linux2;
		struct {
			__u8	h_i_frag;	/* Fragment number */
			__u8	h_i_fsize;	/* Fragment size */
			__le16	h_i_mode_high;
			__le16	h_i_uid_high;
			__le16	h_i_gid_high;
			__le32	h_i_author;
		} hurd2;
		struct {
			__u8	m_i_frag;	/* Fragment number */
			__u8	m_i_fsize;	/* Fragment size */
			__u16	m_pad1;
			__u32	m_i_reserved2[2];
		} masix2;
	} osd2;				/* OS dependent 2 */
};

注:在Linux内核的/include/uapi/linux/types.h中定义了__le16、__le32的具体类型

/* The kernel doesn't use this legacy form, but user space does */
#define __bitwise__ __bitwise

typedef __u16 __bitwise __le16;
typedef __u16 __bitwise __be16;
typedef __u32 __bitwise __le32;
typedef __u32 __bitwise __be32;
typedef __u64 __bitwise __le64;
typedef __u64 __bitwise __be64;

typedef __u16 __bitwise __sum16;
typedef __u32 __bitwise __wsum;

__le16:小端序(little endian)、16位、无符号、整型

__le32:小端序、32位、无符号、整型

结论: Linux存储文件时是将文件的内容(data block)和文件的属性(inode)分开存储的

简单讲讲ext2_inode中的一些成员变量:

i_mode:

高4位: 文件类型(例如块设备、目录等)

《Linux内核探秘 深入解析文件系统和设备驱动的架构与设计》的表2-1列出了一些文件类型:

低12位: 权限位(rwx) + 特殊位(setuid、setgid、sticky)

        setuid和setgid参见《Operating Systems Design and Implementation Third Edition》的CHAPTER1 INTRODUCTION的1.4.3 System Calls for File Management

        sticky粘滞位,在OS9.【Linux】基本权限(下)文章讲过

i_uid: 用户ID

i_gid: 组ID

i_size: 文件大小(单位字节)

i_links_count: 记录inode的硬链接数(有关软硬链接的问题后面会提到,本文不讲)

Data block Bitmap和inode Bitmap: 意图很明显,因为都含有"bitmap",即位图,作用分别是快速寻找到对应的Data block和inode的位置,因为操作系统中存储文件和删除文件比较频繁,如何快速判断数据块是否被使用,可以使用位图将比特位的位置和块号映射起来,可根据比特位的内容表示该数据块有没有被使用

ACM时间(i_atime、i_ctime、i_mtime): 这3个是时间戳,分别表示访问时间、最后改变时间、最后修改时间

i_dtime: 文件删除时间

i_blocks: 记录分配给该文件的磁盘块总数,因为一个文件可能不止占用一个磁盘块

★注意: EXT2文件系统的inode编号是有限的

在Linux上,格式化为EXT2文件系统可以使用mke2fs或者mkfs.ext2命令,inode编号的个数在分区被格式化时就已经写死了,无法修改! 但可以用mke2fs命令的-N(大写)选项来直接设置inode编号的个数

参考资料: https://unix.stackexchange.com/questions/26598/how-can-i-increase-the-number-of-inodes-in-an-ext4-filesystem

重点解释i_block[EXT2_N_BLOCKS]

i_block是指向数据块的指针数组,而Data Block数据块编号的数字可以填到i_block数组中,Linux内核中对EXT2_N_BLOCKS的定义:

来自Linux 6.17 release内核的fs/ext2/ext2.h

/*
 * Constants relative to the data blocks
 */
#define	EXT2_NDIR_BLOCKS		12
#define	EXT2_IND_BLOCK			EXT2_NDIR_BLOCKS
#define	EXT2_DIND_BLOCK			(EXT2_IND_BLOCK + 1)
#define	EXT2_TIND_BLOCK			(EXT2_DIND_BLOCK + 1)
#define	EXT2_N_BLOCKS			(EXT2_TIND_BLOCK + 1)

那么:

    EXT2_N_BLOCKS
 ⇌ (EXT2_TIND_BLOCK + 1)
 ⇌ ((EXT2_DIND_BLOCK + 1) + 1)
 ⇌ (((EXT2_IND_BLOCK + 1) + 1)+ 1)
 ⇌ (((EXT2_NDIR_BLOCKS + 1) + 1)+ 1)
 ⇌ (((12 + 1) + 1)+ 1)
 ⇌ 15

格式化好的带有EXT2文件系统分区的磁盘会有许多数据块(data block),那么如何利用这些数据块来存储文件呢?

EXT2文件系统给出的方法: 使用4个级别的索引

直接索引

ext2.h中定义

#define	EXT2_NDIR_BLOCKS		12

NDIR是Number of DIRect blocks的缩写,,即i_block[0]~i_block[11]这些直接索引元素,它们存储的是数据块的块号,即直接指向存储文件内容的数据块

间接索引(一级间接索引)

#define	EXT2_IND_BLOCK			EXT2_NDIR_BLOCKS //下标为12

IND是INDirect的缩写,即间接索引,也称为一级间接索引,即i_block[12]

如果文件比较大,即前12个元素指向的所有数据块都存不下,那么就要动用间接索引,即i_block[12],其指向的块存储文件内容的数据块号的表,换句话说: 一个数据块可以存储多个块号

二级间接索引

#define	EXT2_DIND_BLOCK			(EXT2_IND_BLOCK + 1)

DIND是Double INDirect的缩写,是二级间接索引,即i_block[13],如果文件比较大,即前13个索引都存不下,那么就要动用二级间接索引块(Double INDirect block)

二级间接索引指向的数据块存储的是一级间接索引表,如下图演示

三级间接索引

#define	EXT2_TIND_BLOCK			(EXT2_DIND_BLOCK + 1)

TIND是Triple INDirect的缩写,即三级间接索引,即i_block[14]

如果文件非常大,导致前14个元素都存不下,那么就要动用三级间接索引

以此类推,三级间接索引指向的数据块存储的是二级间接索引表,这样能存储更多的数据

三个级别的索引的关系图

来自The Linux Programming Interface书的Chapter 14 File System 14.2 Disk and Partitions 作者: Michael Kerrisk

哥伦比亚大学的https://www.cs.columbia.edu/~junfeng/13fa-w4118/lectures/l25-fs-linux.pdf

从结构上看,是一棵多叉树,该树的叶子结点是存储文件内容的数据块,这样一看,使用三级索引能存储更多的文件内容

特别提醒

注: NMU大学的A Non-Technical Look Inside the EXT2 File System文章https://euclid.nmu.edu/~rappleto/Classes/CS426/Notes/FileSystems/EXT2/的解释是错误的:

Inodes and Such
Each file on disk is associated with exactly one inode. The inode stores important information about the file including the create and modify times, the permissions on the file, and the owner of the file. Also stored is the type of file (regular file, directory, device file like /dev/ttyS1, etc) and where the file is stored on disk.

The data in the file is not stored in the inode itself. Instead, the inode points to the location of the data on disk. There are fifteen pointers to data blocks within each inode. However, this does not mean that a file can only be fifteen blocks long. Instead, a file can be millions of blocks long, thanks to the indirect way that data pointers point to data.

The first thirteen pointers point directly to blocks containing file data. If the file is thirteen or fewer blocks long, then the file's data is pointed to directly by pointers within each inode, and can be accessed quickly. The fourteenth pointer is called the indirect pointer, and points to a block of pointers, each one of which points to data on the disk. The fifteenth pointer is called the doubly indirect pointer, and points at a block containing many pointers to blocks each of which points at data on the disk. Perhaps the picture below will make things clear.

从内核代码可以看出,不是前13个指针直接指向文件的数据块,而是前12个,即i_block[0]~i_block[11]这些直接索引元素

而且给的图也有问题:

下一篇文章将继续讲解EXT2文件系统

更多推荐