一、知识点归纳
1. EXT2文件系统
多年来,Linux 一直使用EXT2(Card等1995)作为默认文件系统。EXT3(EXT3,2014)是EXT2的扩展。EXT3中增加的主要内容是一个日志文件,它将文件系统的变更 记录在日志中°日志可在文件系统崩溃时更快地从错误中恢复。没有错误的EXT3文件系 统与EXT2文件系统相同。EXT3的最新扩展是EXT4 ( Cao等2007)。EXT4的主要变化是磁盘块的分配。在EXT4中,块编号为48位。EXT4不是分配不连续的磁盘块,而是分配连续的磁盘块区,称为区段。除了这些细微的更改之外,文件系统结构和文件操作保持不变。本书的目的是讲授文件系统的原理°主要目标并非实现大的文件存储容量,而是重点论述文件系统设计和实现的原则,强调简单性以及与Linux的兼容性。为此,我们以ETX2作为文件系统。
2. EXT2文件系统数据结构
- 超级块(Superblock):包含文件系统的元数据信息,如块大小、inode数量等。
- 块组描述符(Block Group Descriptor):描述块组的信息,如块位图、inode位图、inode表的位置。
- 块位图和inode位图:用于跟踪块和inode的分配情况。
- 索引节点表(Inode Table):存储文件和目录的元数据信息。
使用mke2fs
命令可以在设备上创建EXT2文件系统,示例命令如下:
mke2fs [-b blksize -N ninodes] device nblocks
其中,blksize
指定块大小,ninodes
指定inode数量,device
是目标设备,nblocks
是块数量。
在EXT2文件系统上,可以执行多种文件操作。以下是一些示例:
2.1 创建文件夹
使用mkdir
命令可以创建文件夹,例如:
mkdir myfolder
2.2 创建文件
使用touch
命令可以创建空文件,例如:
touch myfile.txt
2.3 复制文件
使用cp
命令可以复制文件到指定位置,例如:
cp sourcefile.txt destination/
2.4移动/重命名文件
使用mv
命令可以移动文件到新位置或重命名文件,例如:
mv oldfile.txt newfile.txt
2.5 删除文件
使用rm
命令可以删除文件,例如:
rm myfile.txt
2.6 查看文件内容
使用cat
命令可以查看文件的内容,例如:
cat myfile.txt
3.邮差算法
邮差算法用于将街区和房子的地址映射到线性地址,以便外星人或其他系统能够理解和处理这些地址。它的原理是:
- 对于给定的街区地址BA,线性地址LA可以计算为:LA = N * block + house。
- 对于给定的线性地址LA,可以计算街区地址BA为:BA = (LA / N, LA % N)。
以下是一个示例代码,演示如何使用邮差算法将逻辑块地址转换为磁盘块地址。这里使用了一个简单的C函数 block_to_chs
来执行转换,假设一个虚拟的CHS磁盘,其中柱面、磁头、扇区都以0开始计数。
#include <stdio.h>
// 定义虚拟磁盘的参数
#define NUM_CYLINDERS 100
#define NUM_HEADS 4
#define NUM_SECTORS_PER_TRACK 10
// 将逻辑块号转换为CHS地址
void block_to_chs(int logical_block, int *cylinder, int *head, int *sector) {
*cylinder = logical_block / (NUM_HEADS * NUM_SECTORS_PER_TRACK);
int remaining = logical_block % (NUM_HEADS * NUM_SECTORS_PER_TRACK);
*head = remaining / NUM_SECTORS_PER_TRACK;
*sector = remaining % NUM_SECTORS_PER_TRACK;
}
int main() {
int logical_block = 523; // 例如,要转换的逻辑块号
int cylinder, head, sector;
block_to_chs(logical_block, &cylinder, &head, §or);
printf("Logical Block: %d\n", logical_block);
printf("CHS Address: Cylinder %d, Head %d, Sector %d\n", cylinder, head, sector);
return 0;
}
在这个示例中,block_to_chs
函数接受逻辑块号作为输入,并计算出相应的柱面(cylinder)、磁头(head)和扇区(sector)。这个函数假设一个虚拟的CHS磁盘,其中柱面、磁头、扇区都以0开始计数。
4.遍历EXT2文件系统树
遍历EXT2文件系统树的方法可以分为以下几个步骤。在这个过程中,我们将使用文件系统的数据结构和文件操作来获取文件和目录的信息。
-
读取超级块信息:
- 打开设备文件,这可以是磁盘分区或磁盘镜像文件。
- 读取超级块信息,验证是否为EXT2文件系统。超级块包含文件系统的关键参数,如块大小、块组数量、索引节点数量等。
-
读取根目录索引节点:
- 使用超级块信息找到根目录的索引节点(通常是索引节点号2)。
- 读取根目录的索引节点,获取根目录的数据块地址。
-
遍历根目录:
- 从根目录的数据块开始,逐个读取目录条目(
dir_entry
)。 - 目录条目中包含了子文件和子目录的信息,包括文件名和对应的索引节点号。
- 遍历根目录的所有目录条目,处理每个子文件和子目录。
- 从根目录的数据块开始,逐个读取目录条目(
-
递归遍历子目录:
- 如果一个目录条目指向一个子目录,那么需要递归进入这个子目录并重复步骤3。
- 递归的基本情况是当目录条目中的文件名为
"."
或".."
时,它们分别表示当前目录和父目录,应该被跳过。
-
处理文件和目录信息:
- 对于每个文件和目录,你可以获取它们的属性和信息,如文件名、文件类型、大小、权限等。
- 可以将这些信息保存在一个数据结构中,以供后续使用或显示给用户。
5、基本文件系统
type.h
文件包含了EXT2文件系统的数据结构类型,如超级块、组描述符、索引节点和目录条目结构。global.c
文件包含文件系统的全局变量,如内存中的INODE、挂载表、打开文件表和PROC结构体。- 实用程序函数包括
get_block
和put_block
用于读写文件系统块,iget
和iput
用于管理内存中的INODE,以及getino
用于根据路径名查找索引节点号。
6、文件系统的级别
文件系统的实现分为三个级别,每个级别处理文件系统的不同部分:
- 1级文件系统函数处理文件和路径的基本操作,如创建、删除、打开、关闭等。
- 2级文件系统函数涉及文件的读取和写入操作。
- 3级文件系统函数负责文件系统的挂载、卸载和文件保护等高级操作。
这些级别的划分有助于模块化文件系统的实现,使其更容易理解和维护。
7、文件系统项目的扩展
简单的EXT2文件系统使用1KB块大小,只有一个磁盘块组它可以轻松进行以下扩展。
-
多个组:组描述符的大小为32字节对于1KB大小的块,一个块可能包含
1024/32=32组描述符。32个组的文件系统大小可以扩展为32*8=256MB -
4KB大小的块:对于4KB大小的块和一个组,文件系统大小应为48=32MB。
对于一个组描述符块,文件系统可能有128个组,可将文件系统大小扩展到12832=4GB
对于2个组描述符块,文件系统大小为8GB等。大多数扩展都很简单,适合用于编程项目。 -
管道文件:管道可实现为普通文件,这些文件遵循管道的读/写协议。此方案的优点是:它统一了管道和文件索引节点,并允许可被不相关进程使用的命名管道。为支持快速读/写操作,管道内容应在内存中,比如在 RAMdisk中。必要时,读者可将命名管道实现为FIFO文件。
-
I/O缓冲:在编程项目中,每个磁盘块都是接读写的。这会产生过多的物理磁盘I/O操作。为提高效率,实际文件系统通常使用一系列I缓冲区作为磁盘块的缓存内存。文件系统的IO缓冲将会在第12章中讨论,但是可把它合并到文件系统.
二、ChatGpt提问
三、实践及代码托管
(1)手动实现mkdir
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
int main(void) {
int status;
printf("Please input the name of directory: ");
char s[100];
// 使用fgets安全地获取用户输入
if (fgets(s, sizeof(s), stdin) != NULL) {
// 使用strtok去掉输入中的换行符
s[strcspn(s, "\n")] = '\0';
status = mkdir(s, 0777); // 创建目录,0777表示具有最高权限
if (!status) {
printf("Directory created\n");
} else {
printf("Unable to create directory\n");
}
// 使用ls命令来显示目录内容
printf("Contents of the directory:\n");
char ls_command[200];
snprintf(ls_command, sizeof(ls_command), "ls -l %s", s);
system(ls_command);
} else {
printf("Error reading input\n");
}
return 0;
}
(2)手动实现rmdir
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
int deleteDirectory(const char *path) {
int status = rmdir(path);
return status;
}
int main(void) {
int status;
printf("Please input the name of directory: ");
char s[100];
// 使用fgets安全地获取用户输入
if (fgets(s, sizeof(s), stdin) != NULL) {
// 使用strtok去掉输入中的换行符
s[strcspn(s, "\n")] = '\0';
status = deleteDirectory(s);
if (!status) {
printf("\n\nDirectory deleted\n");
} else {
perror("Unable to delete directory");
}
// 使用ls命令来显示删除后的目录内容
printf("Contents of the current directory:\n");
system("ls -l");
} else {
printf("Error reading input\n");
}
return 0;
}
代码已托管至gitee,具体链接:https://gitee.com/wang-yuxuan333/123.git
分别为mymkdir.txt myrmdir.txt
四、问题及解决
原代码的一些函数库已经不被使用,通过ChatGpt修改代码,最终成功运行。