• 学习笔记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命令 查看目录和文件容量

  • 相关阅读:
    OA系统
    高考
    眼压高
    国家职业资格目录,即“专业技术人员职业资格项目”和“技能人员职业资格项目”,介绍
    hexo 添加图床
    《漂向北方》Namewee
    mysql主从架构
    【20220621】稻花香
    【20220614】生活目标变了
    【20220615】连岳摘抄
  • 原文地址:https://www.cnblogs.com/stx3220665294/p/15418298.html
Copyright © 2020-2023  润新知