EXT2文件系统
- EXT2文件系统数据结构
- 邮差算法
- 教材编程实例
- 超级块
- 索引节点
- 块组描述符
- 文件系统项目的扩展
超级块重要字段:
__u32 s_inodes_count://文件系统中节点总数
__u32 s_blocks_count://文件系统中块总数
__u32 s_r_blocks_count//为超级用户保留的块数
__u32 s_free_blocks_count//文件系统中空闲块总数
__u32 s_mtime://文件系统的挂接时间
__u32 s_wtime://最后一次对该超级块进行写操作的时间
__u32 s_magic://魔数签名,用于确定文件系统版本的标志
__u32 s_inodes_per_group:表示每个组块的inode数目,查找文件inode所在块的位置很重要
__u32 s_mnt_count://文件系统挂接计数
__u32 s_max_mnt_count://文件系统最大挂接计数
__u32 s_state://文件系统的状态
文件系统项目的扩展:
简单的EXT2文件系统使用IKB块大小,只有一个磁盘块组。它可以轻松进行以下扩展。
1.多个组:组描述符的大小为32字节”对于1KB大小的块,一个块可能包含 1024/32 = 32组描述符。32个组的文件系统大小可以扩展为32*8 = 256MB.
2.4KB大小的块:对于4KB大小的块和一个组,文件系统大小应为48 = 32MB。对于一个组描述符块,文件系统可能有128个组,可将文件系统大小扩展到12832 = 4GB。 对于2个组描述符块,文件系统大小为8GB等。大多数扩展都很简单,适合用于编程项目。
3.管道文件:管道可实现为普通文件,这些文件遵循管道的读/写协议。此方案的优点是:它统一了管道和文件索引节点,并允许可被不相关进程使用的命名管道。为支持快速读/写操作,管道内容应在内存中,比如在RAMdisk中。必要时,读者可将命名管道实现为FIFO文件.
4.I/O缓冲:在编程项目中,每个磁盘块都是直接读写的。这会产生过多的物理磁盘I/O操作。为提髙效率,实际文件系统通常使用一系列I/O缓冲区作为磁盘块的缓存内存。
遍历EXT2系统文件树:
算法
1.读取超级块;
2.读取块组描述符块(1 + s_first_data_block),以访问组0描述符。并从块组描述符的bg_inode_table条目中找到索引节点的起始块编号,将其成为InodesBeginBlock;
3.读取InodesBeginBlock,获取/的索引节点,即INODE#2;
4.将路径名标记为组件字符串,假设组件数量为恥例如,如果路径名=/a/b/c,则组 件字符串是“a”“b”“c”,其中n = 3。用name[0], namefl],…,name[n-l]来表示组件
5.从(3)中的根索引节点开始,在其数据块中搜索name[0]。(5)。目录索引节点的每个数据块都包含 以下形式的dir entry结构体:
(ino rec_len name_len NAME] [ino rec_len name_len NAME)
其中NAME是一系列nlen字符,不含终止NULLO对于每个数据块,将该块读入内存 并使用dir_entry *dp指向加载的数据块。然后使用name」en将NAME提取为字符串,并与 namefO]进行比较。如果它们不匹配,贝U通过以下代码转到下一个dir_entry:
dp * (dir_entry *)((char *)dp + dp->rec_len))
继续搜索。如果存在name[0],则可以找到它的dir_entry,从而找到它的索引节点号。
6.使用索引节点号ino来定位相应的索引节点。回想前面的内容,ino从1开始计数。 使用邮差算法计算包含索引节点的磁盘块及其在该块中的偏移量。
blk = (ino - 1) * INODES_PER_BLOCK + InodesBeginBlock;
offset = (ino - 1) % INODES_PER_BLOCK;
然后在索引节点中读取/a,从中确定它是否是一个目录(DIR)。如果/a不是目录,则 不能有/a/b,因此搜索失败。如果它是目录,并且有更多需要搜索的组件,那么继续搜索 下一个组件name[l]o现在的问题是:在索引节点中搜索/a的name[l],与第(5 )步完全 相同。
7.由于(5) - (6)步将会重复n次,所以最好编写一个搜索函数
u32 search(INODE *inodePtr, char *name)
(
// search for name in the data blocks of current DIR inode
// if found, return its ino; else return 0
)
8.调用search()n次,n为组件数量。
实践内容:教材实例编程superblock.c:
代码:
/*********** superblock.c program ************/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/io.h>
#include <ext2fs/ext2_fs.h>
// Cypedef u8, ul6, u32 SUPER for convenience typedef typedef typedef
typedef unsigned char u8;
typedef unsigned short ul6;
typedef unsigned int u32;
typedef struct ext2_super_block SUPER;
SUPER *sp;
char buf[1024];
int fd, blksize, inodesize;
int print(char *s, u32 x)
{
printf("%-30s = %8d
", s, x);
}
int super(char *device)
{
fd = open(device, O_RDONLY);
if (fd < 0)
{
printf("open %s failed
", device);
exit(1);
}
lseek(fd, (long)1024*1, 0); // block 1 or offset 1024
read(fd, buf, 1024);
sp = (SUPER *)buf; // as a super block structure
// check for EXT2 FS magic number
printf("%-30s = %8x ", "s_magic", sp->s_magic);
if (sp->s_magic != 0xEF53)
{
printf("NOT an EXT2 FS
"); exit(2);
}
printf("EXT2 FS OK
");
print("s_inodes_count", sp->s_inodes_count);
print("s_blocks _count", sp->s_blocks_count);
print("s_r_blocks_count", sp->s_r_blocks_count);
print("s_free_inodes_count", sp->s_free_inodes_count);
print("s_free_blocks_count", sp->s_free_blocks_count);
print("s_first_data_blcok", sp->s_first_data_block);
print("s_log_block_s i z e", sp->s_log_block_size);
print("s_blocks_per_group", sp->s_blocks_per_group);
print("s_inodes_per_group", sp-> s_inodes_per_group);
print("s_mnt_count", sp->s_mnt_count);
print("s_max_mnt_count", sp-> s_max_mnt_count);
printf("%-30s = %8x
", "s_magic", sp->s_magic);
printf ("s_mtime = %s
", ctime ((const time_t *)&sp->s_mtime));
printf ("s_wtime = %s", ctime ((const time_t *)&sp->s_wtime));
blksize = 1024 * (1 << sp->s_log_block_size);
printf("block size = %d
", blksize);
printf("inode size = %d
", sp->s_inode_size);
}
char *device = "vdisk"; // default device name
int main(int argc, char *argv[])
{
if (argc>1)
device = argv[1];
super(device);
}
邮差算法
这一部分的邮差算法,以我个人的理解就是将一个二维存在的数据转变为线性的记录,以书中的例子进行举例,每个房子都同时具有街区与房子两个编号BA =(街区,房子),而邮差算法就是要将其转变为线性地址LA。
- 1.C语言中的Test- Set-Clear位
我们可用C语言结合使用邮差算法和位屏蔽来进行下面的操作
.TST a bit to 1 or 0 : if (buf[i] & (i<<j))
.SET a bit to 0 : (buf[i] |= (i<<j));
.CLR a bit to 0 : (buf[i] &= (i<<j));
注意,一些C语言编译器允许在结构体中指定位,如:
struct bits{
unsigned int bit0 : 1; // bit0 field is a single bit
unsigned int bit123 :3; // bit123 field is a range of 3 bits
unsigned int otherbits: 27; // other bits field has 27 bits
unsigned int bit31 1 ; // bit31 is the highest bit
}var;
该结构体将 var.定义为一个32位无符号整数,具有单独的位或位范围。那么,var.bit0=0;将1赋值给第0位,则有var.bit123=5;将101赋值给第1位到第3位等。但是,生成的代码仍然依赖于邮差算法和位屏蔽来访问各个位。我们可以用邮差算法直接操作位图中的位,无须定义复杂的C语言结构体。
- 2.将索引节点号转换为磁盘上的索引节点
在 EXT2文件系统中,每个文件都有一个唯一的索引节点结构。在文件系统磁盘上,索引节点从 inode table 块开始。每个磁盘块包含
INODES_PER_BLOCK = BLOCK_SIZE/sizeof(INODE)
个索引节点。每个索引节点都有一个唯一的索引节点号,ino=1,2,…,从1开始线性计数。
已知一个ino,如1234,那么哪个磁盘块包含该索引节点,以及哪个索引节点在该块中呢?我们需要知道磁盘块号,因为需要通过块来读/写一个真正的磁盘。
block =(ino - 1)/INODES_PER_BLOCK + inode_table;
inode =(ino - 1)% INODES_PER_BLOCK;
同样,将EXT2 文件系统中的双重和三重间接逻辑块号转换为物理块号也依赖于邮差算法。
将线性磁盘块号转换为 CHS=(柱面、磁头、扇区)格式∶软盘和旧硬盘使用CHS寻址,但文件系统始终使用线性块寻址。在调用BIOS INT13时,可用该算法将磁盘块号转换为CHS。
文件系统的级别
文件系统的实现分为三个级别。每个级别处理文件系统的不同部分。这使得实现过程模块化,更容易理解。在文件系统的实现过程中,FS目录包含实现 EXT2 文件系统的文件。
第1级别实现了基本文件系统树,包含以下文件,实现了指定函数。
使用第1级别FS函数的用户命令程序有:
mkdir,creat,mkond,rmdir,link,symlink,rm,ls,cd,pwd等
第2级别实现了文件内容的读/写函数。
第3级别实现了文件系统的挂载、卸载和文件保护。