Linux——ext2文件系统
- ext2文件系统
- 宏观认识
- 一、磁盘分区与格式化
- 二、块组(Block Group)结构
- 三、文件系统特性
- 文件名与目录名与inode
- 一、inode的作用原理
- 二、文件与目录名与inode的关系
- 路径
- 一,路径解析
- 二,路径缓存
- 三,实验笔记:创建、挂载与卸载虚拟磁盘镜像
ext2文件系统
宏观认识
ext2文件系统的整体构成主要包括以下几个方面:
一、磁盘分区与格式化
磁盘被划分为多个分区,每个分区在格式化时会采用特定的文件系统格式,ext2即为其中之一。
格式化过程会在磁盘上写入管理存储布局的信息,使得操作系统能够识别和使用该分区。
二、块组(Block Group)结构
ext2文件系统将整个分区划分为若干个同样大小的块组,每个块组包含以下关键组成部分:
超级块(Super Block):
描述整个分区的文件系统信息,如块大小、文件系统版本号、inode/block的总量、使用量、剩余量以及文件系统的格式与相关信息等。
超级块在每个块组的开头都有一份拷贝(第一个块组必须有,后面的块组可以没有),以保证在部分扇区出现物理问题的情况下,文件系统仍能正常工作。
块组描述符表(GDT, Group Descriptor Table):
由多个块组描述符组成,每个块组描述符存储一个块组的描述信息。
如块组中inode表和数据块的起始位置、空闲的inode和数据块数量等。
块位图(Block Bitmap):
描述整个块组中哪些块已被使用,哪些块仍然空闲。
一个bit代表一个块,bit为1表示该块已用,bit为0表示该块空闲。
inode位图(inode Bitmap):
与块位图类似,描述整个块组中哪些inode已被使用,哪些inode仍然空闲。
inode表(inode Table):
以列表形式保存了文件的元数据信息,包括文件大小、权限、所有者、创建/修改/访问时间等。
每个文件或目录都有一个inode,通过inode的id可以确定inode数据结构在块组中的位置和inode表位置。
数据块(Data Block):
实际记录文件的内容。
文件数据存储在数据块中,对于目录,其下的所有文件名和目录名也存储在数据块中。
三、文件系统特性
ext2文件系统支持长文件名(最长255字符),并为超级用户预留了一部分磁盘空间以防止系统崩溃。
它采用了块组的概念,将磁盘空间高效地组织和管理起来。
每个文件或目录都有一个对应的inode,这使得文件系统能够高效地管理和访问文件。
文件名与目录名与inode
一、inode的作用原理
inode的定义:
inode(索引节点)是Linux文件系统中的一个核心概念,它存储了文件系统对象(如文件和目录)的所有元数据,除了文件名以外的所有信息几乎都存储在inode中。
inode的结构:
- inode号码:每个inode都有一个唯一的号码,用于在文件系统中唯一标识该文件或目录。
- 文件类型与权限:定义了文件的类型(如普通文件、目录、符号链接等)和权限(如读、写、执行权限)。
- 文件大小:记录文件的数据大小,以字节为单位。
- 链接数:表示有多少个硬链接指向这个inode。
- UID和GID:文件所有者的用户ID和组ID。
- 时间戳:包括文件的最后访问时间、最后修改时间和inode状态的最后改变时间。
- 数据块指针:指向文件数据实际存储位置的指针,这些指针是文件系统定位和访问文件数据的关键。
inode的工作原理:
当文件系统需要访问文件时,它会首先通过文件名查找对应的inode号码。
然后,文件系统使用inode号码访问inode表,获取文件的元数据。
这些元数据包含了文件数据块的位置信息,文件系统利用这些信息直接访问文件数据,而不需要遍历整个文件系统。
inode的使用例子:
使用ls -i命令列出目录中的文件和子目录的inode号码,观察不同文件和目录的inode号码是否唯一。
创建一个新文件,并使用stat命令查看其inode信息,包括inode号码、文件大小、权限等。
通过硬链接和软链接的练习,理解inode在链接管理中的作用。硬链接是指向同一个inode的多个文件名,而软链接则是一个包含所链接文件名称的特殊文件。
二、文件与目录名与inode的关系
关系概述:
- 文件名和目录名是用户可见的标识符,用于引用和访问文件或目录。
- inode是文件系统内部的元数据结构,用于存储文件或目录的所有重要信息,并指向文件数据的实际存储位置。
- 文件名和目录名与inode之间通过文件系统内部的机制进行关联和映射。
实际操作中的关系体现:
当用户通过文件名或目录名访问文件时,文件系统会根据这些名称查找对应的inode。
一旦找到对应的inode,文件系统就可以使用inode中的元数据来访问文件数据。
文件的重命名操作实际上只是更改了文件名与inode之间的映射关系,而不影响inode本身或文件数据。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "Usage: %s <directory>\n",argv[0]);
exit(EXIT_FAILURE);
}
DIR *dir = opendir(argv[1]); // 系统调⽤,⾃⾏查阅
if (!dir)
{
perror("opendir");
exit(EXIT_FAILURE);
}
struct dirent *entry;
while ((entry = readdir(dir)) != NULL)
{ // 系统调⽤,⾃⾏查阅
// Skip the "." and ".." directory entries
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..")
== 0)
{
continue;
}
printf("Filename: %s, Inode: %lu\n", entry->d_name, (unsignedlong)entry->d_ino);
}
closedir(dir);
return 0;
}
路径
一,路径解析
在Linux系统中,路径解析是操作系统内核和用户空间程序共同协作的一个过程,用于将用户提供的文件路径转换为内核可以识别的文件系统上的具体位置。路径解析主要涉及到路径名的查找和转换,它分为绝对路径解析和相对路径解析两种。以下是路径解析的原理和过程:
1. 绝对路径解析
绝对路径(或称为完整路径)是从根目录(/)开始的完整路径。例如,/home/user/file.txt。
解析过程:
- 根目录识别:首先识别路径的起始点是根目录(/)。
- 目录遍历:从根目录开始,依次遍历路径中的每一个目录名。例如,对于/home/user/file.txt,系统会首先找到根目录下的home目录,然后在home目录下找到user目录,最后在user目录下找到file.txt文件。
- 文件/目录查找:在遍历过程中,系统会调用文件系统相关的API来查找每一个目录项(即目录或文件)。如果某个目录项不存在,解析过程将失败,返回错误。
- 权限检查:在遍历过程中,系统还会检查当前用户对每个目录的访问权限。如果没有足够的权限,解析过程也会失败。
2. 相对路径解析
相对路径是相对于当前工作目录的路径。例如,…/file.txt表示当前目录的上一级目录中的file.txt文件。
解析过程:
- 当前工作目录识别:首先识别当前的工作目录。这通常是通过环境变量PWD(当前工作目录的绝对路径)来实现的。
- 路径规范化:将相对路径转换为从当前工作目录开始的绝对路径。这涉及到处理…(上一级目录)和.(当前目录)等特殊符号。
- 目录遍历和文件/目录查找:与绝对路径解析类似,从规范化后的绝对路径开始遍历和查找。
- 权限检查:同样,系统会检查当前用户对每个目录的访问权限。
3. 符号链接处理
在路径解析过程中,如果遇到符号链接(symbolic link),系统会解析该链接所指向的实际路径,并继续解析。这可能导致路径解析过程变得复杂,因为符号链接可以指向其他目录或文件,甚至形成循环链接(即符号链接指向自身或经过一系列链接后回到起点)。
4. 错误处理
在路径解析过程中,如果遇到任何错误(如目录不存在、文件不存在、权限不足等),系统会返回相应的错误代码或错误信息。
二,路径缓存
1. Linux中的“目录”与“文件”
在Linux磁盘中,从底层存储的角度看,实际上并不存在真正的“目录”。Linux文件系统只保存文件的属性(如权限、所有者、大小等)和内容。我们通常所说的“目录”,实际上是一种特殊的文件,它包含了其他文件和目录的名称及对应的inode号(inode是文件系统用于唯一标识文件的数据结构)。因此,Linux中的所有实体,无论是文件还是目录,都是通过inode来管理和访问的。
2. 路径解析与缓存
在Linux中,访问任何文件都需要从根目录(/)开始进行路径解析。然而,如果每次访问文件都从头开始解析路径,效率将非常低。为了优化这一过程,Linux引入了路径缓存机制。
路径缓存主要通过dentry结构体来实现。dentry(directory entry)是Linux内核中用于表示目录项的数据结构。它包含了文件的名称、父目录指针、inode指针等信息。当系统需要访问某个文件时,会首先在路径缓存中查找对应的dentry。如果找到了,就可以直接获取文件的inode和属性,无需再次进行磁盘I/O操作。
3. dentry结构体详解
dentry结构体在Linux内核中扮演着至关重要的角色。它维护了文件系统的树状路径结构,并提供了快速查找和访问文件的能力。dentry结构体包含多个字段,用于存储文件的名称、父目录指针、inode指针、哈希链表节点等信息。
- d_count:引用计数,用于管理dentry的生命周期。
- d_flags和d_lock:用于保护dentry结构的标志位和自旋锁。
- d_inode:指向文件的inode结构体的指针,用于获取文件的属性。
- d_hash、d_parent和d_name:分别用于哈希查找、指向父目录和存储文件名称。
- d_lru、d_child和d_subdirs:分别用于LRU缓存管理、子目录链表和子目录列表。
- 其他字段:如d_alias、d_time、d_op等,用于支持其他功能,如inode别名列表、时间戳和特定于文件系统的操作等。
4. 路径缓存的工作原理
当系统需要访问某个文件时,会按照以下步骤进行路径解析和缓存管理:
- 路径查找:从根目录开始,依次遍历路径中的每个目录项,并在路径缓存中查找对应的dentry。
- 缓存命中:如果找到了对应的dentry,且其关联的inode有效,则直接返回该inode和文件的属性。
- 缓存未命中:如果未找到对应的dentry,或者dentry已失效(如inode号不匹配),则需要从磁盘中读取目录项信息,创建新的dentry,并将其添加到路径缓存中。
- LRU管理:为了保持路径缓存的高效性,Linux使用LRU算法来管理dentry的淘汰。当缓存空间不足时,最近最少使用的dentry将被淘汰出缓存。
- 哈希加速:为了提高查找效率,Linux还使用了哈希表来存储dentry。通过哈希查找,可以快速定位到目标dentry。
三,实验笔记:创建、挂载与卸载虚拟磁盘镜像
实验目的
- 理解磁盘镜像文件的创建:通过dd命令,学习如何创建一个指定大小和内容的磁盘镜像文件。
- 掌握文件系统格式化:使用mkfs命令,在创建的磁盘镜像文件上写入文件系统。
- 了解挂载与卸载文件系统:通过mount和umount命令,学习如何将磁盘镜像文件挂载为文件系统,并安全地卸载它。
- 认识循环设备:理解循环设备(loop device)的作用,以及它如何允许将文件作为块设备来使用。
实验步骤与用途
创建磁盘镜像文件
dd if=/dev/zero of=./disk.img bs=1M count=5
目的:创建一个大小为5MB的磁盘镜像文件disk.img,该文件由全零字节填充。
用途:模拟一个物理磁盘分区,用于后续的文件系统格式化和挂载操作。
格式化磁盘镜像文件
mkfs.ext4 disk.img
目的:在disk.img上创建一个EXT4文件系统。
用途:使磁盘镜像文件具备存储和组织文件的能力,类似于实际的磁盘分区。
创建挂载点目录
mkdir /mnt/mydisk
目的:在文件系统中创建一个空目录/mnt/mydisk,作为挂载点的目标位置。
用途:挂载磁盘镜像文件时,该目录将作为访问挂载文件系统的入口。
查看当前挂载的文件系统
df -h
目的:在挂载磁盘镜像文件之前,查看当前系统中已挂载的文件系统。
用途:确认挂载操作前后的变化,以及验证挂载是否成功。
挂载磁盘镜像文件
sudo mount -t ext4 ./disk.img /mnt/mydisk/
目的:将disk.img挂载到/mnt/mydisk目录。
用途:通过挂载点访问和操作磁盘镜像文件上的文件系统。
再次查看当前挂载的文件系统
df -h
目的:验证disk.img是否成功挂载,并查看其挂载点、大小和使用情况。
用途:确认挂载操作的结果,以及磁盘镜像文件上的文件系统状态。
注意循环设备
在挂载过程中,注意到/dev/loop0被用作挂载的块设备。
循环设备:一种伪设备,允许将文件作为块设备来使用。在本实验中,disk.img被当作一个块设备挂载到/mnt/mydisk。
卸载磁盘镜像文件
sudo umount /mnt/mydisk
目的:安全地卸载挂载在/mnt/mydisk上的磁盘镜像文件。
用途:释放挂载点目录和循环设备资源,避免数据损坏或系统不稳定。
验证卸载操作
再次运行df -h命令,确认disk.img已从挂载的文件系统列表中消失。
实验总结
通过本次实验,我们成功创建了一个磁盘镜像文件,并在其上格式化了EXT4文件系统。随后,我们将该磁盘镜像文件挂载到指定的挂载点目录,并通过挂载点访问和操作了文件系统。最后,我们安全地卸载了挂载的磁盘镜像文件。