学习笔记5

本章知识点如下:

EXT2文件系统的历史与现状

EXT2文件数据结构

各个级别文件系统函数算法

融合所有级别于一个项目中

 

EXT2文件系统历史

多年来,Linux一直使用EXT2(Card等1995)作为默认文件系统。EXT3(EXT3,2014)是EXT2的扩展。EXT3中增加的主要内容是一个日志文件,它将文件系统的变更记录在日志中。日志可在文件系统崩溃时更快地从错误中恢复。没有错误的EXT3文件系统与EXT2文件系统相同。EXT3的最新扩展是EXT4(Cao等2007)。EXT4的主要变化是磁盘块的分配。在EXT4中,块编号为48位。EXT4不是分配不连续的磁盘块,而是分配连续的磁盘块区,称为区段。除了这些细微的更改之外,文件系统结构和文件操作保持不变。

 

EXT2文件系统数据结构

1. 通过 mkfs 创建虚拟磁盘
在Linux下,命令
mke2fs [-b blkesize -N ninodes] device nblocks
在设备上创建一个带有nblocks个块(每个块大小为blksize字节)和ninodes个索引节点的EXT2文件系统。设备可以是真实设备,也可以是虚拟磁盘文件。如果未指定blksize,则默认块大小为1KB。如果未指定ninoides,mke2fs将根据 nblocks 计算一个默认的ninodes数。得到的EXT2文件系统可在Linux中使用。

2. 虚拟磁盘布局

一个磁盘可以分成多个分区,每个分区必须先用格式化工具格式化成某种格式的文件系统,才能存储文件,在格式化的过程中会在磁盘上写一些管理存储布局的信息。

  • Block#0:
    引导块,文件系统不会使用它。它用于容纳从磁盘引导操作系统的引导程序。
  • Block#1:
    超级块(在硬盘分区中字节偏移量为1024)。用于容纳关于整个文件系统的信息。
    超级块中一些重要字段
struct et2_super block {
  u32 s_inodes_count;        /* Inodes count */
  u32 s_blocks_count;        /* Blocks count */
  u32 s_r_blocks_count;      /* Reserved blocks count */
  u32 s_free blocks_count;   /* Free blocks count */
  u32 s_free_inodes_count;   /* Free inodes count */
  u32 s_first_data_block;    /* First Data Block */
  u32 s_log block_size;      /* Block size */
  u32 s_log_cluster_size;    /* Al1ocation cluster size */
  u32 s_blocks per_group;    /* # Blocks per group * /
  u32 s_clusters per_group;  /* # Fragments per group */
  u32 s_inodes_per_group;    /* # Inodes per group * /
  u32s_mtime;                /* Mount time * /
  u32s_wtime;                /* write time */
  u16s_mnt_count;            /* Mount coune* /
  s16 s_max_ntcount;         /* Maximal mount count */
  u16 B_magic;               /* Magic signature */
  //more non-essential fields
  u16 s_inode_size;          /* size of inode structure*/
}

s_first_data_block:0表示4KB块大小,1表示1KB块大小。它用于确定块组描述符的起始块,即s_first_data_block +1。
s_log_block_size确定文件块大小,为1KB*(2**s_log_block_size),例如0表示 1KB块大小,1表示2KB块大小,2表示4KB块大小,等等。最常用的块大小是用于小文件系统的1KB和用于大文件系统的4KB。
s_mnt_count:已挂载文件系统的次数。当挂载计数达到max_mount_count时,fsck会话将被迫检查文件系统的一致性。
s_magic是标识文件系统类型的幻数。EXT2/3/4文件系统的幻数是OxEF53。

  • Block#2
    块组描述符(硬盘上的s_first_data_blocks-1)
    EXT2将磁盘块分成几个组,每个组有8192个块(硬盘上的大小为32K)
struct ext2_group_dese {
  u32 bg_b1ock_bitmap; //Bmap bloak number
  u32 bg_inode_bitmap; //Imap block number
  u32 bg_inode_table;  //Inodes begin block number
  u16 bg_free_blocks_count; //THESE are OBVIOUS
  u16 bg_free_inodes_count;
  u16 bg_used_dirs_count;
  u16 bg_pad; // ignore these
  u32 bg_reserved[3];
};

由于一个软盘只有1440个块,B2只包含一个块组描述符。其余的都是0。在有大量块组的硬盘上,块组描述符可以跨越多个块。块组描述符中最重要的字段是bg_block_bitmap.bg_inode_bitmap和 bg_inode_table,它们分别指向块组的块位图、索引节点位图和索引节点起始块。对于Linux格式的EXT2文件系统,保留了块3到块7。所以,bmap=8,imap=9,inode_table= 10。

  • Block #8 块位图(Bmap)
    用来表示某种项的位序列。0表示对应项处于FREE状态,1表示处于IN_USE状态。1个软盘有1440个块,但Block#0未被文件系统使用,所以对应位图只有1439个有效位,无效位视作IN_USE处理,设置为1.

  • Block #9 索引节点位图(Imap)
    一个索引节点就是用来代表一个文件的数据结构。EXT2文件系统是使用有限数量的索引节点创建的。各索引节点的状态用B9 中 Imap中的一个位表示。在EXT2 FS 中,前10个索引节点是预留的。所以,空EXT2FS的Imap 以10个1开头,然后是0。无效位再次设置为1。

  • Block #10 索引(开始)节点块(bg_inode_table)
    每个文件都用一个128字节(EXT4中的是256字节)的独特索引节点结构体表示。

struct ext2_inode{
  
u16 i_mode;
// 16 bits =|tttt|ugs|rwx|rwx|rwxl 
  u16 i_uid;
// owner uid 
  u32 i_size;
// file size in bytes 
  
u32 i_atime;// time fields in seconds
  u32 i_ctime;
// since 00:00:00,1-1-1970
  u32 i_mtime;
  u32 i_dtime;
  u16 i_gid;
// group ID 
  u16 i_links_count;// hard-link count 

  
u32 i_blocks;// number of 512-byte sectors
  
u32 i_flags;// IGNORE

  u32 i_reserved1;
// IGNORE
  u32 i_block[15];// See details below 
  u32 i_pad[7];
// for inode size = 128 bytes

}

在索引节点结构体中,i_mode 为ul6或2字节无符号整数。

       |4
   |3  |9          |

i_mod -|tttt|ugs|zvwkzvwxrws|

在i mode 字段中,前4位指定了文件类型,例如∶tt=1000表示REG文件,0100表示 DIR文件等。接下来的3位ugs表示文件的特殊用法。最后9位是用于文件保护的rwx 权限位。

i_size字段表示文件大小(以字节为单位)。各时间字段表示自1970年1月1日0时0分0秒以来经过的秒数。所以,每个时间字段都是一个非常大的无符号整数。可借助以下库函数将它们转换为日历形式;
char *ctime(&time_field)
将指针指向时间字段,然后返回一个日历形式的字符串。
例如:printf("%s",ctime(&inode.i_atime);// note∶ pass & of time field prints i_atime in calendar form.

i_block[15]数组包含指向文件磁盘块的指针,这些磁盘块有∶

      1. 直接块∶iblock[0] 至i block[11],指向直接磁盘块。
    • 2. 间接块∶i block[12]指向一个包含256个块编号(对于1KB BLKSIZE)的磁盘块,每个块编号指向一个磁盘块。
    • 3. 双重间接块∶i block[13]指向一个指向256个块的块,每个块指向 256个磁盘块。
      1. 三重间接块∶i_block[14]是三重间接块。对于"小型"EXT2文件系统,可以忽略它。索引节点大小(128或256)用于平均分割块大小(1KB或4KB),所以,每个索引节点块都包含整数个索引节点。在简单的EXT2文件系统中,索引节点的数量是184个(Linux默认值)。索引节点块数等于184/8=23个。因此,索引节点块为B10至B32。每个索引节点都有一个唯一的索引节点编号,即索引节点在索引节点块上的位置+1。注意,索引节点位置从0开始计数,而索引节点编号从1开始计数。0索引节点编号表示没有索引节点。根目录的索引节点编号为2。同样,磁盘块编号也从1开始计数,因为文件系统从未使用块0。块编号0表示没有磁盘块。
    • 数据块
      紧跟在索引节点块后面的是文件存储数据块。假设有184个索引节点,第一个实际数据块是B33,它就是根目录/的i_block[0]。
      根据不同文件类型有一下几种情况:
      (1)对于常规文件,文件的数据存储在数据块中。
      (2)对于目录,该目录下的所有文件名和目录名存储在数据块中,除文件名之外,ls -l命令中看到的信息则存储在inode中;目录也是一种文件,是一种特殊类型的文件。
      (3)对于符号链接,如果目标路径名较短则直接保存在inode中以便更快的查找,如果目标路径名较长则分配一个数据块来保存。
      (4)设备文件,FIFO和socket等特殊文件没有数据块,设备文件的主设备号和次设备号保存在inode中。
      数据块寻址

 

邮差算法

例:一个城市有M个街区,编号从0到M-1。每个街区有N座房子,编号从0到N-1。每座房子有一个唯一的街区地址,用(街区,房子)表示,其中0≤街区<M,0≤房子<N。外人不熟悉该街区寻址方案,采用线性方法将房子地址编为0,1,…,N-1,N,N+1等。已知某个街区地址BA=(街区,房子),如何转换为线性地址LA,相反,已知线性地址,如何转换为街区地址?
LA=N*block + house;
BA=(LA/N, LA%N); (只有从0开始计数转换才有效)

编历EXT2文件系统树

编历一个EXT2文件系统和一个文件的路径名,例如/a/b/c,问题是如何找到这个文件。查找文件相当于查找其索引节点。

1编历算法

(1)读取超级块;
(2)读取块组描述符块(1+s_first_data_block),以访问组0描述符;
(3)读取InodeBeginBlock,获取/的索引节点,即INODE#2;
(4)将路径名标记为组件字符串,假设组件数量为n;
(5)从(3)中的根索引节点开始,在其数据块中搜索name[0];
(6)使用索引节点号ino来定位相应的索引节点。Ino从1开始计数,使用邮差算法计算包含索引节点的磁盘块及其在该块中的偏移量;
(7)由于(5)~(6)步将会重复n次,所以最好编写一个搜索函数。

2将路径名转换为索引节点

已知一个包含EXT2文件系统和路径名的设备,例如/a/b/c/d,编写一个C函数:INODE *path2inode(int fd, char *pathname);返回一个指向文件索引节点的INODE指针;如果文件不可访问,则返回0。

3显示索引节点磁盘块

编写一个C程序showblock,可打印文件的所有磁盘块(编号);

 

问题与解决思路:

书上对于EXT2文件系统的数据结构的讲解只有较为简单的讲述,对于实际操作方式还是有些不清楚

参考这篇博文:https://www.cnblogs.com/ant-colonies/p/11005511.html

 

1 超级块(Super Blook)

 

超级块的数据结构如下:

 

struct ext2_super_block

 

主要的几项描述有:s_inodes_count / s_blocks_count(该分区inode/block总数),s_free_inodes_count / s_free_blocks_count(该分区空闲inode/block数),s_first_data_block(数据块中的起始块位置),s_log_block_size(块大小),s_blocks_per_group(每个块组的块数),s_magic(魔数)

 

struct ext2_super_block {
	__u32	s_inodes_count;		/* Inodes count */
	__u32	s_blocks_count;		/* Blocks count */
	...
	__u32	s_free_blocks_count;	/* Free blocks count */
	__u32	s_free_inodes_count;	/* Free inodes count */
	__u32	s_first_data_block;	/* First Data Block */
	__u32	s_log_block_size;	/* Block size */
	...
	__u32	s_blocks_per_group;	/* # Blocks per group */
	...
	__u16	s_magic;		/* Magic signature */     # ext2的魔数为0xEF53
	...

 

说明:每个块组中都有一份超级块的拷贝。当文件系统挂载时,通常只有块组0中的超级块(主备份)会被读取,其他的块组中的超级块只是作为备份,以防文件系统的崩溃。

 

 

 

2 组描述符表(Group Descriptors Table,GDT)

 

超级块之后就是组描述符表,是由该分区所有的块的组描述符(Group Descriptor)组成的,每个块组描述符记录了本块组的inode/block bitmap和inode table等。块组描述符数据结构如下所示:

 

struct ext2_group_desc
{
	__u32	bg_block_bitmap;	/* Blocks bitmap block */
	__u32	bg_inode_bitmap;	/* Inodes bitmap block */
	__u32	bg_inode_table;		/* Inodes table block */
	__u16	bg_free_blocks_count;	/* Free blocks count */
	__u16	bg_free_inodes_count;	/* Free inodes count */
	__u16	bg_used_dirs_count;	/* Directories count */
	__u16	bg_pad;
	__u32	bg_reserved[3];
};

 

说明:与超级类似,组描述符表也存在各块组中紧接着超级块,其目的与超级块一样,作为备份,防止文件系统的崩溃。

 

 

 

3 块位图和inode位图(block/inode bitmap)

 

位图(bitmap)是位(bit)的序列,每一个位代表该位图所在块组中的一个特定的数据块(block bitmap)或inode table中一个特定的inode(inode bitmap)。当bit为0时,表示对应的block/inode空闲;为1时,表示已被占用。

 

 

位图始终索引其所在的块组,并且block位图和inode位图均为1block大小,从而限制了该块组的大小。例如一般的block大小为1024bytes,因此一个块组总共有1024*8个block。

 

 

 

4 inode表(inode table)

 

inode表由一系列连续的block块组成,每个块中都预定义了一定数量的inode。inode表的起始块位置(块号)存储在组描述符的bg_inode_table字段中。

 

系统对inode表中的inode进行编号,从1开始,inode的数据结构被定义在ext2_fs.h文件的struct ext2_inode函数中:

 

struct ext2_inode

 

以下是inode数据结构中比较重要的字段:

 

struct ext2_inode {
        __u16   i_mode;         /* File type and access rights */
        __u16   i_uid;          /* Low 16 bits of Owner Uid */
        __u32   i_size;         /* Size in bytes */
        __u32   i_atime;        /* Access time */
        __u32   i_ctime;        /* Creation time */
        __u32   i_mtime;        /* Modification time */
        __u32   i_dtime;        /* Deletion Time */
        __u16   i_gid;          /* Low 16 bits of Group Id */
        __u16   i_links_count;  /* Links count */
        __u32   i_blocks;       /* Blocks count */
        __u32   i_flags;        /* File flags */
	...
	__u32   i_block[EXT2_N_BLOCKS];  /* Pointers to blocks */
	...
};

 

i_mode字段中包含了文件的类型和文件的访问权限,被定义在宏文件macro (sys/stat.h)中。

 

Sign Type Macro
- Regular file S_ISREG(m)
d Directory S_ISDIR(m)
c Character Device   S_ISCHR(m)
b Block Device S_ISBLK(m)
f Fifo S_ISIFO(m)
s Socket S_ISSOCK(m)
l Symbolic Link S_ISLNK(m)

 

Domain Read Write Exec All
User S_IRUSR S_IWUSR S_IXUSR S_IRWXU
Group S_IRGRP S_IWGRP S_IXGRP S_IRWXG
All S_IROTH S_IWOTH S_IXOTH S_IRWXO

 

 

 

i_blocks是该inode指向的文件已使用的block数量;/*

 

175   * Constants relative to the data blocks

 

176   */

 

177  #define EXT2_NDIR_BLOCKS        12

 

178  #define EXT2_IND_BLOCK          EXT2_NDIR_BLOCKS

 

179  #define EXT2_DIND_BLOCK         (EXT2_IND_BLOCK + 1)

 

180  #define EXT2_TIND_BLOCK         (EXT2_DIND_BLOCK + 1)

 

181  #define EXT2_N_BLOCKS           (EXT2_TIND_BLOCK + 1)

 

i_block[]数组中有15个指针,它们所代表的含义如下:

 

  •  i_block[0..11] point directly to the first 12 data blocks of the file.              # 序列号0-11的12个元素(指针)指向文件开头的12个数据块
  •  i_block[12] points to a single indirect block                                               # 第13号元素指向单索引间接块
  •  i_block[13] points to a double indirect block                                              # 第14号元素指向双索引间接块
  •  i_block[14] points to a triple indirect block                                                 # 第15号元素指向三索引间接块

 

由此我们可以计算出ext2文件系统单个文件的最大容量(假设block大小为1K),数组i_block[]的长度为32bit/8=4byte:

 

  • 直接索引:12 指针
  • 单间接索引:1024/4=256个直接索引,256 指针
  • 双间接索引:1024/4=256个单间接索引,256*256 指针              
  • 三间接索引:1024/4=256个双间接索引,256*256*256 指针      

 

因此可得到12K+256K+64M+16G,即大致为16G。如果block的大小为4K,则文件最大可为4T。(注意:真正决定文件大小的是底层的寄存器,寄存器的位数决定了其寻址的能力)

 

 

 

5 inode表中的inode指向的目录文件

 

inode指向的目录文件需加以注意,我们可以通过测试S_ISDIR(mode) macro来加以识别:

 

if (S_ISDIR(inode.i_mode)) ... 

 

假设inode指向的块是目录实体/home,目录中的内容包含了一系列的文件名和指向inode表中的对应的,如下图:

 

 

目录文件的数据结构如下:

 

struct ext2_dir_entry_2 {
	__u32	inode;			/* Inode number */
	__u16	rec_len;		/* Directory entry length */
	__u8	name_len;		/* Name length */
	__u8	file_type;
	char	name[EXT2_NAME_LEN];	/* File name */
};

 

字段file_type总共有0-7可能的值,分别代表: 

 

 

 

目录内容中的各项(实体)的大小是非固定的,大小取决于文件名称的长度。文件名称最大长度为EXT2_NAME_LEN的值,一般为255;文件名称的实际长度存放于字段name_len;rec_len存储的是本目录实体的大小,该字段自然也就决定了下一目录实体的位置了。

 

  Example of EXT2 directory

 

注意:目录中的inode号,指向的是inode表中的inode,指向data block的是i_block[EXT2_N_BLOCKS]的数组中的指针。

实践内容与截图:

 

1、查看文件操作系统

 

以上示例效果中还有一个叫做buffers memory的内存区块,这个区块缓存了文件系统的部分Inode信息,这样保证了操作系统不会随时到文件系统上寻找Inode——优化文件系统的读性能。cache memory区块和buffers memory区块由操作系统自行管理,它们只会使用当前没有被应用程序占用的空闲内存。当应用程序请求新的内存区块且空闲内存不够时,操作系统会释放部分cache memory区块或者buffers memory区块。

 

 

2、df、du命令学习

 

df(英文全拼:disk free)用于显示目前在Linux系统上的文件系统磁盘使用情况统计。

 

 

 

第二列指定一个特定的文件系统1K-块1K是1024字节为单位的总内存。用和可用列正在使用中,分别指定的内存量。
使用列指定使用的内存的百分比,而最后一栏"安装在"指定的文件系统的挂载点。
用一个-i选项的df命令的输出显示inode信息而非块使用量

du命令 查看目录和文件容量

 

posted @ 2021-10-17 21:38  20191212  阅读(51)  评论(0编辑  收藏  举报